aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/getopt.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/getopt.d
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std/getopt.d')
-rw-r--r--libphobos/src/std/getopt.d1857
1 files changed, 1857 insertions, 0 deletions
diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d
new file mode 100644
index 0000000..5beddcc
--- /dev/null
+++ b/libphobos/src/std/getopt.d
@@ -0,0 +1,1857 @@
+// Written in the D programming language.
+
+/**
+Processing of command line options.
+
+The getopt module implements a $(D getopt) function, which adheres to
+the POSIX syntax for command line options. GNU extensions are
+supported in the form of long options introduced by a double dash
+("--"). Support for bundling of command line options, as was the case
+with the more traditional single-letter approach, is provided but not
+enabled by default.
+
+Copyright: Copyright Andrei Alexandrescu 2008 - 2015.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP erdani.org, Andrei Alexandrescu)
+Credits: This module and its documentation are inspired by Perl's $(HTTP
+ perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
+ D's $(D getopt) is simpler than its Perl counterpart because $(D
+ getopt) infers the expected parameter types from the static types of
+ the passed-in pointers.
+Source: $(PHOBOSSRC std/_getopt.d)
+*/
+/*
+ Copyright Andrei Alexandrescu 2008 - 2015.
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+module std.getopt;
+
+import std.exception; // basicExceptionCtors
+import std.traits;
+
+/**
+Thrown on one of the following conditions:
+$(UL
+ $(LI An unrecognized command-line argument is passed, and
+ $(D std.getopt.config.passThrough) was not present.)
+ $(LI A command-line option was not found, and
+ $(D std.getopt.config.required) was present.)
+)
+*/
+class GetOptException : Exception
+{
+ mixin basicExceptionCtors;
+}
+
+static assert(is(typeof(new GetOptException("message"))));
+static assert(is(typeof(new GetOptException("message", Exception.init))));
+
+/**
+ Parse and remove command line options from a string array.
+
+ Synopsis:
+
+---------
+import std.getopt;
+
+string data = "file.dat";
+int length = 24;
+bool verbose;
+enum Color { no, yes };
+Color color;
+
+void main(string[] args)
+{
+ auto helpInformation = getopt(
+ args,
+ "length", &length, // numeric
+ "file", &data, // string
+ "verbose", &verbose, // flag
+ "color", "Information about this color", &color); // enum
+ ...
+
+ if (helpInformation.helpWanted)
+ {
+ defaultGetoptPrinter("Some information about the program.",
+ helpInformation.options);
+ }
+}
+---------
+
+ The $(D getopt) function takes a reference to the command line
+ (as received by $(D main)) as its first argument, and an
+ unbounded number of pairs of strings and pointers. Each string is an
+ option meant to "fill" the value referenced by the pointer to its
+ right (the "bound" pointer). The option string in the call to
+ $(D getopt) should not start with a dash.
+
+ In all cases, the command-line options that were parsed and used by
+ $(D getopt) are removed from $(D args). Whatever in the
+ arguments did not look like an option is left in $(D args) for
+ further processing by the program. Values that were unaffected by the
+ options are not touched, so a common idiom is to initialize options
+ to their defaults and then invoke $(D getopt). If a
+ command-line argument is recognized as an option with a parameter and
+ the parameter cannot be parsed properly (e.g., a number is expected
+ but not present), a $(D ConvException) exception is thrown.
+ If $(D std.getopt.config.passThrough) was not passed to $(D getopt)
+ and an unrecognized command-line argument is found, a $(D GetOptException)
+ is thrown.
+
+ Depending on the type of the pointer being bound, $(D getopt)
+ recognizes the following kinds of options:
+
+ $(OL
+ $(LI $(I Boolean options). A lone argument sets the option to $(D true).
+ Additionally $(B true) or $(B false) can be set within the option separated
+ with an "=" sign:
+
+---------
+ bool verbose = false, debugging = true;
+ getopt(args, "verbose", &verbose, "debug", &debugging);
+---------
+
+ To set $(D verbose) to $(D true), invoke the program with either
+ $(D --verbose) or $(D --verbose=true).
+
+ To set $(D debugging) to $(D false), invoke the program with
+ $(D --debugging=false).
+ )
+
+ $(LI $(I Numeric options.) If an option is bound to a numeric type, a
+ number is expected as the next option, or right within the option separated
+ with an "=" sign:
+
+---------
+ uint timeout;
+ getopt(args, "timeout", &timeout);
+---------
+
+ To set $(D timeout) to $(D 5), invoke the program with either
+ $(D --timeout=5) or $(D --timeout 5).
+ )
+
+ $(LI $(I Incremental options.) If an option name has a "+" suffix and is
+ bound to a numeric type, then the option's value tracks the number of times
+ the option occurred on the command line:
+
+---------
+ uint paranoid;
+ getopt(args, "paranoid+", &paranoid);
+---------
+
+ Invoking the program with "--paranoid --paranoid --paranoid" will set $(D
+ paranoid) to 3. Note that an incremental option never expects a parameter,
+ e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set
+ $(D paranoid) to 42; instead, $(D paranoid) is set to 2 and "42" is not
+ considered as part of the normal program arguments.
+ )
+
+ $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as
+ a string is expected as the next option, or right within the option
+ separated with an "=" sign:
+
+---------
+ enum Color { no, yes };
+ Color color; // default initialized to Color.no
+ getopt(args, "color", &color);
+---------
+
+ To set $(D color) to $(D Color.yes), invoke the program with either
+ $(D --color=yes) or $(D --color yes).
+ )
+
+ $(LI $(I String options.) If an option is bound to a string, a string is
+ expected as the next option, or right within the option separated with an
+ "=" sign:
+
+---------
+string outputFile;
+getopt(args, "output", &outputFile);
+---------
+
+ Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
+ will set $(D outputFile) to "myfile.txt". If you want to pass a string
+ containing spaces, you need to use the quoting that is appropriate to your
+ shell, e.g. --output='my file.txt'.
+ )
+
+ $(LI $(I Array options.) If an option is bound to an array, a new element
+ is appended to the array each time the option occurs:
+
+---------
+string[] outputFiles;
+getopt(args, "output", &outputFiles);
+---------
+
+ Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
+ "--output myfile.txt --output yourfile.txt" will set $(D outputFiles) to
+ $(D [ "myfile.txt", "yourfile.txt" ]).
+
+ Alternatively you can set $(LREF arraySep) as the element separator:
+
+---------
+string[] outputFiles;
+arraySep = ","; // defaults to "", separation by whitespace
+getopt(args, "output", &outputFiles);
+---------
+
+ With the above code you can invoke the program with
+ "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".)
+
+ $(LI $(I Hash options.) If an option is bound to an associative array, a
+ string of the form "name=value" is expected as the next option, or right
+ within the option separated with an "=" sign:
+
+---------
+double[string] tuningParms;
+getopt(args, "tune", &tuningParms);
+---------
+
+ Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
+ $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ].
+
+ Alternatively you can set $(LREF arraySep) as the element separator:
+
+---------
+double[string] tuningParms;
+arraySep = ","; // defaults to "", separation by whitespace
+getopt(args, "tune", &tuningParms);
+---------
+
+ With the above code you can invoke the program with
+ "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6".
+
+ In general, the keys and values can be of any parsable types.
+ )
+
+ $(LI $(I Callback options.) An option can be bound to a function or
+ delegate with the signature $(D void function()), $(D void function(string
+ option)), $(D void function(string option, string value)), or their
+ delegate equivalents.
+
+ $(UL
+ $(LI If the callback doesn't take any arguments, the callback is
+ invoked whenever the option is seen.
+ )
+
+ $(LI If the callback takes one string argument, the option string
+ (without the leading dash(es)) is passed to the callback. After that,
+ the option string is considered handled and removed from the options
+ array.
+
+---------
+void main(string[] args)
+{
+ uint verbosityLevel = 1;
+ void myHandler(string option)
+ {
+ if (option == "quiet")
+ {
+ verbosityLevel = 0;
+ }
+ else
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ }
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+}
+---------
+
+ )
+
+ $(LI If the callback takes two string arguments, the option string is
+ handled as an option with one argument, and parsed accordingly. The
+ option and its value are passed to the callback. After that, whatever
+ was passed to the callback is considered handled and removed from the
+ list.
+
+---------
+int main(string[] args)
+{
+ uint verbosityLevel = 1;
+ bool handlerFailed = false;
+ void myHandler(string option, string value)
+ {
+ switch (value)
+ {
+ case "quiet": verbosityLevel = 0; break;
+ case "verbose": verbosityLevel = 2; break;
+ case "shouting": verbosityLevel = verbosityLevel.max; break;
+ default :
+ stderr.writeln("Unknown verbosity level ", value);
+ handlerFailed = true;
+ break;
+ }
+ }
+ getopt(args, "verbosity", &myHandler);
+ return handlerFailed ? 1 : 0;
+}
+---------
+ )
+ ))
+)
+
+Options_with_multiple_names:
+Sometimes option synonyms are desirable, e.g. "--verbose",
+"--loquacious", and "--garrulous" should have the same effect. Such
+alternate option names can be included in the option specification,
+using "|" as a separator:
+
+---------
+bool verbose;
+getopt(args, "verbose|loquacious|garrulous", &verbose);
+---------
+
+Case:
+By default options are case-insensitive. You can change that behavior
+by passing $(D getopt) the $(D caseSensitive) directive like this:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.caseSensitive,
+ "foo", &foo,
+ "bar", &bar);
+---------
+
+In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar",
+"--FOo", "--bAr", etc. are rejected.
+The directive is active until the end of $(D getopt), or until the
+converse directive $(D caseInsensitive) is encountered:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.caseSensitive,
+ "foo", &foo,
+ std.getopt.config.caseInsensitive,
+ "bar", &bar);
+---------
+
+The option "--Foo" is rejected due to $(D
+std.getopt.config.caseSensitive), but not "--Bar", "--bAr"
+etc. because the directive $(D
+std.getopt.config.caseInsensitive) turned sensitivity off before
+option "bar" was parsed.
+
+Short_versus_long_options:
+Traditionally, programs accepted single-letter options preceded by
+only one dash (e.g. $(D -t)). $(D getopt) accepts such parameters
+seamlessly. When used with a double-dash (e.g. $(D --t)), a
+single-letter option behaves the same as a multi-letter option. When
+used with a single dash, a single-letter option is accepted. If the
+option has a parameter, that must be "stuck" to the option without
+any intervening space or "=":
+
+---------
+uint timeout;
+getopt(args, "timeout|t", &timeout);
+---------
+
+To set $(D timeout) to $(D 5), use either of the following: $(D --timeout=5),
+$(D --timeout 5), $(D --t=5), $(D --t 5), or $(D -t5). Forms such as $(D -t 5)
+and $(D -timeout=5) will be not accepted.
+
+For more details about short options, refer also to the next section.
+
+Bundling:
+Single-letter options can be bundled together, i.e. "-abc" is the same as
+$(D "-a -b -c"). By default, this option is turned off. You can turn it on
+with the $(D std.getopt.config.bundling) directive:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.bundling,
+ "foo|f", &foo,
+ "bar|b", &bar);
+---------
+
+In case you want to only enable bundling for some of the parameters,
+bundling can be turned off with $(D std.getopt.config.noBundling).
+
+Required:
+An option can be marked as required. If that option is not present in the
+arguments an exception will be thrown.
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.required,
+ "foo|f", &foo,
+ "bar|b", &bar);
+---------
+
+Only the option directly following $(D std.getopt.config.required) is
+required.
+
+Passing_unrecognized_options_through:
+If an application needs to do its own processing of whichever arguments
+$(D getopt) did not understand, it can pass the
+$(D std.getopt.config.passThrough) directive to $(D getopt):
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.passThrough,
+ "foo", &foo,
+ "bar", &bar);
+---------
+
+An unrecognized option such as "--baz" will be found untouched in
+$(D args) after $(D getopt) returns.
+
+Help_Information_Generation:
+If an option string is followed by another string, this string serves as a
+description for this option. The $(D getopt) function returns a struct of type
+$(D GetoptResult). This return value contains information about all passed options
+as well a $(D bool GetoptResult.helpWanted) flag indicating whether information
+about these options was requested. The $(D getopt) function always adds an option for
+`--help|-h` to set the flag if the option is seen on the command line.
+
+Options_Terminator:
+A lone double-dash terminates $(D getopt) gathering. It is used to
+separate program options from other parameters (e.g., options to be passed
+to another program). Invoking the example above with $(D "--foo -- --bar")
+parses foo but leaves "--bar" in $(D args). The double-dash itself is
+removed from the argument array unless the $(D std.getopt.config.keepEndOfOptions)
+directive is given.
+*/
+GetoptResult getopt(T...)(ref string[] args, T opts)
+{
+ import std.exception : enforce;
+ enforce(args.length,
+ "Invalid arguments string passed: program name missing");
+ configuration cfg;
+ GetoptResult rslt;
+
+ GetOptException excep;
+ void[][string] visitedLongOpts, visitedShortOpts;
+ getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts);
+
+ if (!rslt.helpWanted && excep !is null)
+ {
+ throw excep;
+ }
+
+ return rslt;
+}
+
+///
+@system unittest
+{
+ auto args = ["prog", "--foo", "-b"];
+
+ bool foo;
+ bool bar;
+ auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b",
+ "Some help message about bar.", &bar);
+
+ if (rslt.helpWanted)
+ {
+ defaultGetoptPrinter("Some information about the program.",
+ rslt.options);
+ }
+}
+
+/**
+ Configuration options for $(D getopt).
+
+ You can pass them to $(D getopt) in any position, except in between an option
+ string and its bound pointer.
+*/
+enum config {
+ /// Turn case sensitivity on
+ caseSensitive,
+ /// Turn case sensitivity off (default)
+ caseInsensitive,
+ /// Turn bundling on
+ bundling,
+ /// Turn bundling off (default)
+ noBundling,
+ /// Pass unrecognized arguments through
+ passThrough,
+ /// Signal unrecognized arguments as errors (default)
+ noPassThrough,
+ /// Stop at first argument that does not look like an option
+ stopOnFirstNonOption,
+ /// Do not erase the endOfOptions separator from args
+ keepEndOfOptions,
+ /// Make the next option a required option
+ required
+}
+
+/** The result of the $(D getopt) function.
+
+$(D helpWanted) is set if the option `--help` or `-h` was passed to the option parser.
+*/
+struct GetoptResult {
+ bool helpWanted; /// Flag indicating if help was requested
+ Option[] options; /// All possible options
+}
+
+/** Information about an option.
+*/
+struct Option {
+ string optShort; /// The short symbol for this option
+ string optLong; /// The long symbol for this option
+ string help; /// The description of this option
+ bool required; /// If a option is required, not passing it will result in an error
+}
+
+private pure Option splitAndGet(string opt) @trusted nothrow
+{
+ import std.array : split;
+ auto sp = split(opt, "|");
+ Option ret;
+ if (sp.length > 1)
+ {
+ ret.optShort = "-" ~ (sp[0].length < sp[1].length ?
+ sp[0] : sp[1]);
+ ret.optLong = "--" ~ (sp[0].length > sp[1].length ?
+ sp[0] : sp[1]);
+ }
+ else if (sp[0].length > 1)
+ {
+ ret.optLong = "--" ~ sp[0];
+ }
+ else
+ {
+ ret.optShort = "-" ~ sp[0];
+ }
+
+ return ret;
+}
+
+@safe unittest
+{
+ auto oshort = splitAndGet("f");
+ assert(oshort.optShort == "-f");
+ assert(oshort.optLong == "");
+
+ auto olong = splitAndGet("foo");
+ assert(olong.optShort == "");
+ assert(olong.optLong == "--foo");
+
+ auto oshortlong = splitAndGet("f|foo");
+ assert(oshortlong.optShort == "-f");
+ assert(oshortlong.optLong == "--foo");
+
+ auto olongshort = splitAndGet("foo|f");
+ assert(olongshort.optShort == "-f");
+ assert(olongshort.optLong == "--foo");
+}
+
+/*
+This function verifies that the variadic parameters passed in getOpt
+follow this pattern:
+
+ [config override], option, [description], receiver,
+
+ - config override: a config value, optional
+ - option: a string or a char
+ - description: a string, optional
+ - receiver: a pointer or a callable
+*/
+private template optionValidator(A...)
+{
+ import std.format : format;
+ import std.typecons : staticIota;
+
+ enum fmt = "getopt validator: %s (at position %d)";
+ enum isReceiver(T) = isPointer!T || (is(T == function)) || (is(T == delegate));
+ enum isOptionStr(T) = isSomeString!T || isSomeChar!T;
+
+ auto validator()
+ {
+ string msg;
+ static if (A.length > 0)
+ {
+ static if (isReceiver!(A[0]))
+ {
+ msg = format(fmt, "first argument must be a string or a config", 0);
+ }
+ else static if (!isOptionStr!(A[0]) && !is(A[0] == config))
+ {
+ msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0);
+ }
+ else foreach (i; staticIota!(1, A.length))
+ {
+ static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) &&
+ !(is(A[i] == config)))
+ {
+ msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i);
+ break;
+ }
+ else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1]))
+ {
+ msg = format(fmt, "a receiver can not be preceeded by a receiver", i);
+ break;
+ }
+ else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1])
+ && isSomeString!(A[i-2]))
+ {
+ msg = format(fmt, "a string can not be preceeded by two strings", i);
+ break;
+ }
+ }
+ static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
+ {
+ msg = format(fmt, "last argument must be a receiver or a config",
+ A.length -1);
+ }
+ }
+ return msg;
+ }
+ enum message = validator;
+ alias optionValidator = message;
+}
+
+@safe pure unittest
+{
+ alias P = void*;
+ alias S = string;
+ alias A = char;
+ alias C = config;
+ alias F = void function();
+
+ static assert(optionValidator!(S,P) == "");
+ static assert(optionValidator!(S,F) == "");
+ static assert(optionValidator!(A,P) == "");
+ static assert(optionValidator!(A,F) == "");
+
+ static assert(optionValidator!(C,S,P) == "");
+ static assert(optionValidator!(C,S,F) == "");
+ static assert(optionValidator!(C,A,P) == "");
+ static assert(optionValidator!(C,A,F) == "");
+
+ static assert(optionValidator!(C,S,S,P) == "");
+ static assert(optionValidator!(C,S,S,F) == "");
+ static assert(optionValidator!(C,A,S,P) == "");
+ static assert(optionValidator!(C,A,S,F) == "");
+
+ static assert(optionValidator!(C,S,S,P) == "");
+ static assert(optionValidator!(C,S,S,P,C,S,F) == "");
+ static assert(optionValidator!(C,S,P,C,S,S,F) == "");
+
+ static assert(optionValidator!(C,A,P,A,S,F) == "");
+ static assert(optionValidator!(C,A,P,C,A,S,F) == "");
+
+ static assert(optionValidator!(P,S,S) != "");
+ static assert(optionValidator!(P,P,S) != "");
+ static assert(optionValidator!(P,F,S,P) != "");
+ static assert(optionValidator!(C,C,S) != "");
+ static assert(optionValidator!(S,S,P,S,S,P,S) != "");
+ static assert(optionValidator!(S,S,P,P) != "");
+ static assert(optionValidator!(S,S,S,P) != "");
+
+ static assert(optionValidator!(C,A,S,P,C,A,F) == "");
+ static assert(optionValidator!(C,A,P,C,A,S,F) == "");
+}
+
+@system unittest // bugzilla 15914
+{
+ bool opt;
+ string[] args = ["program", "-a"];
+ getopt(args, config.passThrough, 'a', &opt);
+ assert(opt);
+ opt = false;
+ args = ["program", "-a"];
+ getopt(args, 'a', &opt);
+ assert(opt);
+ opt = false;
+ args = ["program", "-a"];
+ getopt(args, 'a', "help string", &opt);
+ assert(opt);
+ opt = false;
+ args = ["program", "-a"];
+ getopt(args, config.caseSensitive, 'a', "help string", &opt);
+ assert(opt);
+
+ assertThrown(getopt(args, "", "forgot to put a string", &opt));
+}
+
+private void getoptImpl(T...)(ref string[] args, ref configuration cfg,
+ ref GetoptResult rslt, ref GetOptException excep,
+ void[][string] visitedLongOpts, void[][string] visitedShortOpts, T opts)
+{
+ enum validationMessage = optionValidator!T;
+ static assert(validationMessage == "", validationMessage);
+
+ import std.algorithm.mutation : remove;
+ import std.conv : to;
+ static if (opts.length)
+ {
+ static if (is(typeof(opts[0]) : config))
+ {
+ // it's a configuration flag, act on it
+ setConfig(cfg, opts[0]);
+ return getoptImpl(args, cfg, rslt, excep, visitedLongOpts,
+ visitedShortOpts, opts[1 .. $]);
+ }
+ else
+ {
+ // it's an option string
+ auto option = to!string(opts[0]);
+ if (option.length == 0)
+ {
+ excep = new GetOptException("An option name may not be an empty string", excep);
+ return;
+ }
+ Option optionHelp = splitAndGet(option);
+ optionHelp.required = cfg.required;
+
+ if (optionHelp.optLong.length)
+ {
+ assert(optionHelp.optLong !in visitedLongOpts,
+ "Long option " ~ optionHelp.optLong ~ " is multiply defined");
+
+ visitedLongOpts[optionHelp.optLong] = [];
+ }
+
+ if (optionHelp.optShort.length)
+ {
+ assert(optionHelp.optShort !in visitedShortOpts,
+ "Short option " ~ optionHelp.optShort
+ ~ " is multiply defined");
+
+ visitedShortOpts[optionHelp.optShort] = [];
+ }
+
+ static if (is(typeof(opts[1]) : string))
+ {
+ auto receiver = opts[2];
+ optionHelp.help = opts[1];
+ immutable lowSliceIdx = 3;
+ }
+ else
+ {
+ auto receiver = opts[1];
+ immutable lowSliceIdx = 2;
+ }
+
+ rslt.options ~= optionHelp;
+
+ bool incremental;
+ // Handle options of the form --blah+
+ if (option.length && option[$ - 1] == autoIncrementChar)
+ {
+ option = option[0 .. $ - 1];
+ incremental = true;
+ }
+
+ bool optWasHandled = handleOption(option, receiver, args, cfg, incremental);
+
+ if (cfg.required && !optWasHandled)
+ {
+ excep = new GetOptException("Required option "
+ ~ option ~ " was not supplied", excep);
+ }
+ cfg.required = false;
+
+ getoptImpl(args, cfg, rslt, excep, visitedLongOpts,
+ visitedShortOpts, opts[lowSliceIdx .. $]);
+ }
+ }
+ else
+ {
+ // no more options to look for, potentially some arguments left
+ for (size_t i = 1; i < args.length;)
+ {
+ auto a = args[i];
+ if (endOfOptions.length && a == endOfOptions)
+ {
+ // Consume the "--" if keepEndOfOptions is not specified
+ if (!cfg.keepEndOfOptions)
+ args = args.remove(i);
+ break;
+ }
+ if (!a.length || a[0] != optionChar)
+ {
+ // not an option
+ if (cfg.stopOnFirstNonOption) break;
+ ++i;
+ continue;
+ }
+ if (a == "--help" || a == "-h")
+ {
+ rslt.helpWanted = true;
+ args = args.remove(i);
+ continue;
+ }
+ if (!cfg.passThrough)
+ {
+ throw new GetOptException("Unrecognized option "~a, excep);
+ }
+ ++i;
+ }
+
+ Option helpOpt;
+ helpOpt.optShort = "-h";
+ helpOpt.optLong = "--help";
+ helpOpt.help = "This help information.";
+ rslt.options ~= helpOpt;
+ }
+}
+
+private bool handleOption(R)(string option, R receiver, ref string[] args,
+ ref configuration cfg, bool incremental)
+{
+ import std.algorithm.iteration : map, splitter;
+ import std.ascii : isAlpha;
+ import std.conv : text, to;
+ // Scan arguments looking for a match for this option
+ bool ret = false;
+ for (size_t i = 1; i < args.length; )
+ {
+ auto a = args[i];
+ if (endOfOptions.length && a == endOfOptions) break;
+ if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar))
+ {
+ // first non-option is end of options
+ break;
+ }
+ // Unbundle bundled arguments if necessary
+ if (cfg.bundling && a.length > 2 && a[0] == optionChar &&
+ a[1] != optionChar)
+ {
+ string[] expanded;
+ foreach (j, dchar c; a[1 .. $])
+ {
+ // If the character is not alpha, stop right there. This allows
+ // e.g. -j100 to work as "pass argument 100 to option -j".
+ if (!isAlpha(c))
+ {
+ if (c == '=')
+ j++;
+ expanded ~= a[j + 1 .. $];
+ break;
+ }
+ expanded ~= text(optionChar, c);
+ }
+ args = args[0 .. i] ~ expanded ~ args[i + 1 .. $];
+ continue;
+ }
+
+ string val;
+ if (!optMatch(a, option, val, cfg))
+ {
+ ++i;
+ continue;
+ }
+
+ ret = true;
+
+ // found it
+ // from here on, commit to eat args[i]
+ // (and potentially args[i + 1] too, but that comes later)
+ args = args[0 .. i] ~ args[i + 1 .. $];
+
+ static if (is(typeof(*receiver) == bool))
+ {
+ if (val.length)
+ {
+ // parse '--b=true/false'
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else
+ {
+ // no argument means set it to true
+ *receiver = true;
+ }
+ }
+ else
+ {
+ import std.exception : enforce;
+ // non-boolean option, which might include an argument
+ //enum isCallbackWithOneParameter = is(typeof(receiver("")) : void);
+ enum isCallbackWithLessThanTwoParameters =
+ (is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) &&
+ !is(typeof(receiver("", "")));
+ if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental)
+ {
+ // Eat the next argument too. Check to make sure there's one
+ // to be eaten first, though.
+ enforce(i < args.length,
+ "Missing value for argument " ~ a ~ ".");
+ val = args[i];
+ args = args[0 .. i] ~ args[i + 1 .. $];
+ }
+ static if (is(typeof(*receiver) == enum))
+ {
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(*receiver) : real))
+ {
+ // numeric receiver
+ if (incremental) ++*receiver;
+ else *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(*receiver) == string))
+ {
+ // string receiver
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(receiver) == delegate) ||
+ is(typeof(*receiver) == function))
+ {
+ static if (is(typeof(receiver("", "")) : void))
+ {
+ // option with argument
+ receiver(option, val);
+ }
+ else static if (is(typeof(receiver("")) : void))
+ {
+ static assert(is(typeof(receiver("")) : void));
+ // boolean-style receiver
+ receiver(option);
+ }
+ else
+ {
+ static assert(is(typeof(receiver()) : void));
+ // boolean-style receiver without argument
+ receiver();
+ }
+ }
+ else static if (isArray!(typeof(*receiver)))
+ {
+ // array receiver
+ import std.range : ElementEncodingType;
+ alias E = ElementEncodingType!(typeof(*receiver));
+
+ if (arraySep == "")
+ {
+ *receiver ~= to!E(val);
+ }
+ else
+ {
+ foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
+ *receiver ~= elem;
+ }
+ }
+ else static if (isAssociativeArray!(typeof(*receiver)))
+ {
+ // hash receiver
+ alias K = typeof(receiver.keys[0]);
+ alias V = typeof(receiver.values[0]);
+
+ import std.range : only;
+ import std.string : indexOf;
+ import std.typecons : Tuple, tuple;
+
+ static Tuple!(K, V) getter(string input)
+ {
+ auto j = indexOf(input, assignChar);
+ enforce!GetOptException(j != -1, "Could not find '"
+ ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
+ auto key = input[0 .. j];
+ auto value = input[j + 1 .. $];
+ return tuple(to!K(key), to!V(value));
+ }
+
+ static void setHash(Range)(R receiver, Range range)
+ {
+ foreach (k, v; range.map!getter)
+ (*receiver)[k] = v;
+ }
+
+ if (arraySep == "")
+ setHash(receiver, val.only);
+ else
+ setHash(receiver, val.splitter(arraySep));
+ }
+ else
+ static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof);
+ }
+ }
+
+ return ret;
+}
+
+// 17574
+@system unittest
+{
+ import std.algorithm.searching : startsWith;
+
+ try
+ {
+ string[string] mapping;
+ immutable as = arraySep;
+ arraySep = ",";
+ scope (exit)
+ arraySep = as;
+ string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""];
+ args.getopt("m", &mapping);
+ assert(false, "Exception not thrown");
+ }
+ catch (GetOptException goe)
+ assert(goe.msg.startsWith("Could not find"));
+}
+
+// 5316 - arrays with arraySep
+@system unittest
+{
+ import std.conv;
+
+ arraySep = ",";
+ scope (exit) arraySep = "";
+
+ string[] names;
+ auto args = ["program.name", "-nfoo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "-n", "foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "--name=foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "--name", "foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+}
+
+// 5316 - associative arrays with arraySep
+@system unittest
+{
+ import std.conv;
+
+ arraySep = ",";
+ scope (exit) arraySep = "";
+
+ int[string] values;
+ values = values.init;
+ auto args = ["program.name", "-vfoo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "-v", "foo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "--values=foo=0,bar=1,baz=2"];
+ getopt(args, "values|t", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "--values", "foo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+}
+
+/**
+ The option character (default '-').
+
+ Defaults to '-' but it can be assigned to prior to calling $(D getopt).
+ */
+dchar optionChar = '-';
+
+/**
+ The string that conventionally marks the end of all options (default '--').
+
+ Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an
+ empty string to $(D endOfOptions) effectively disables it.
+ */
+string endOfOptions = "--";
+
+/**
+ The assignment character used in options with parameters (default '=').
+
+ Defaults to '=' but can be assigned to prior to calling $(D getopt).
+ */
+dchar assignChar = '=';
+
+/**
+ The string used to separate the elements of an array or associative array
+ (default is "" which means the elements are separated by whitespace).
+
+ Defaults to "" but can be assigned to prior to calling $(D getopt).
+ */
+string arraySep = "";
+
+private enum autoIncrementChar = '+';
+
+private struct configuration
+{
+ import std.bitmanip : bitfields;
+ mixin(bitfields!(
+ bool, "caseSensitive", 1,
+ bool, "bundling", 1,
+ bool, "passThrough", 1,
+ bool, "stopOnFirstNonOption", 1,
+ bool, "keepEndOfOptions", 1,
+ bool, "required", 1,
+ ubyte, "", 2));
+}
+
+private bool optMatch(string arg, string optPattern, ref string value,
+ configuration cfg) @safe
+{
+ import std.array : split;
+ import std.string : indexOf;
+ import std.uni : toUpper;
+ //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
+ //scope(success) writeln("optMatch result: ", value);
+ if (arg.length < 2 || arg[0] != optionChar) return false;
+ // yank the leading '-'
+ arg = arg[1 .. $];
+ immutable isLong = arg.length > 1 && arg[0] == optionChar;
+ //writeln("isLong: ", isLong);
+ // yank the second '-' if present
+ if (isLong) arg = arg[1 .. $];
+ immutable eqPos = indexOf(arg, assignChar);
+ if (isLong && eqPos >= 0)
+ {
+ // argument looks like --opt=value
+ value = arg[eqPos + 1 .. $];
+ arg = arg[0 .. eqPos];
+ }
+ else
+ {
+ if (!isLong && eqPos == 1)
+ {
+ // argument looks like -o=value
+ value = arg[2 .. $];
+ arg = arg[0 .. 1];
+ }
+ else
+ if (!isLong && !cfg.bundling)
+ {
+ // argument looks like -ovalue and there's no bundling
+ value = arg[1 .. $];
+ arg = arg[0 .. 1];
+ }
+ else
+ {
+ // argument looks like --opt, or -oxyz with bundling
+ value = null;
+ }
+ }
+ //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
+ // Split the option
+ const variants = split(optPattern, "|");
+ foreach (v ; variants)
+ {
+ //writeln("Trying variant: ", v, " against ", arg);
+ if (arg == v || !cfg.caseSensitive && toUpper(arg) == toUpper(v))
+ return true;
+ if (cfg.bundling && !isLong && v.length == 1
+ && indexOf(arg, v) >= 0)
+ {
+ //writeln("success");
+ return true;
+ }
+ }
+ return false;
+}
+
+private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc
+{
+ final switch (option)
+ {
+ case config.caseSensitive: cfg.caseSensitive = true; break;
+ case config.caseInsensitive: cfg.caseSensitive = false; break;
+ case config.bundling: cfg.bundling = true; break;
+ case config.noBundling: cfg.bundling = false; break;
+ case config.passThrough: cfg.passThrough = true; break;
+ case config.noPassThrough: cfg.passThrough = false; break;
+ case config.required: cfg.required = true; break;
+ case config.stopOnFirstNonOption:
+ cfg.stopOnFirstNonOption = true; break;
+ case config.keepEndOfOptions:
+ cfg.keepEndOfOptions = true; break;
+ }
+}
+
+@system unittest
+{
+ import std.conv;
+ import std.math;
+
+ uint paranoid = 2;
+ string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"];
+ getopt(args, "paranoid+", &paranoid);
+ assert(paranoid == 5, to!(string)(paranoid));
+
+ enum Color { no, yes }
+ Color color;
+ args = ["program.name", "--color=yes",];
+ getopt(args, "color", &color);
+ assert(color, to!(string)(color));
+
+ color = Color.no;
+ args = ["program.name", "--color", "yes",];
+ getopt(args, "color", &color);
+ assert(color, to!(string)(color));
+
+ string data = "file.dat";
+ int length = 24;
+ bool verbose = false;
+ args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
+ getopt(
+ args,
+ "length", &length,
+ "file", &data,
+ "verbose", &verbose);
+ assert(args.length == 1);
+ assert(data == "dat.file");
+ assert(length == 5);
+ assert(verbose);
+
+ //
+ string[] outputFiles;
+ args = ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"];
+ getopt(args, "output", &outputFiles);
+ assert(outputFiles.length == 2
+ && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt");
+
+ outputFiles = [];
+ arraySep = ",";
+ args = ["program.name", "--output", "myfile.txt,yourfile.txt"];
+ getopt(args, "output", &outputFiles);
+ assert(outputFiles.length == 2
+ && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt");
+ arraySep = "";
+
+ foreach (testArgs;
+ [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"],
+ ["program.name", "--tune=alpha=0.5,beta=0.6"],
+ ["program.name", "--tune", "alpha=0.5,beta=0.6"]])
+ {
+ arraySep = ",";
+ double[string] tuningParms;
+ getopt(testArgs, "tune", &tuningParms);
+ assert(testArgs.length == 1);
+ assert(tuningParms.length == 2);
+ assert(approxEqual(tuningParms["alpha"], 0.5));
+ assert(approxEqual(tuningParms["beta"], 0.6));
+ arraySep = "";
+ }
+
+ uint verbosityLevel = 1;
+ void myHandler(string option)
+ {
+ if (option == "quiet")
+ {
+ verbosityLevel = 0;
+ }
+ else
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ }
+ args = ["program.name", "--quiet"];
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+ assert(verbosityLevel == 0);
+ args = ["program.name", "--verbose"];
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+ assert(verbosityLevel == 2);
+
+ verbosityLevel = 1;
+ void myHandler2(string option, string value)
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ args = ["program.name", "--verbose", "2"];
+ getopt(args, "verbose", &myHandler2);
+ assert(verbosityLevel == 2);
+
+ verbosityLevel = 1;
+ void myHandler3()
+ {
+ verbosityLevel = 2;
+ }
+ args = ["program.name", "--verbose"];
+ getopt(args, "verbose", &myHandler3);
+ assert(verbosityLevel == 2);
+
+ bool foo, bar;
+ args = ["program.name", "--foo", "--bAr"];
+ getopt(args,
+ std.getopt.config.caseSensitive,
+ std.getopt.config.passThrough,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args[1] == "--bAr");
+
+ // test stopOnFirstNonOption
+
+ args = ["program.name", "--foo", "nonoption", "--bar"];
+ foo = bar = false;
+ getopt(args,
+ std.getopt.config.stopOnFirstNonOption,
+ "foo", &foo,
+ "bar", &bar);
+ assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar");
+
+ args = ["program.name", "--foo", "nonoption", "--zab"];
+ foo = bar = false;
+ getopt(args,
+ std.getopt.config.stopOnFirstNonOption,
+ "foo", &foo,
+ "bar", &bar);
+ assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab");
+
+ args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
+ bool fb1, fb2;
+ bool tb1 = true;
+ getopt(args, "fb1", &fb1, "fb2", &fb2, "tb1", &tb1);
+ assert(fb1 && fb2 && !tb1);
+
+ // test keepEndOfOptions
+
+ args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
+ getopt(args,
+ std.getopt.config.keepEndOfOptions,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args == ["program.name", "nonoption", "--", "--baz"]);
+
+ // Ensure old behavior without the keepEndOfOptions
+
+ args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
+ getopt(args,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args == ["program.name", "nonoption", "--baz"]);
+
+ // test function callbacks
+
+ static class MyEx : Exception
+ {
+ this() { super(""); }
+ this(string option) { this(); this.option = option; }
+ this(string option, string value) { this(option); this.value = value; }
+
+ string option;
+ string value;
+ }
+
+ static void myStaticHandler1() { throw new MyEx(); }
+ args = ["program.name", "--verbose"];
+ try { getopt(args, "verbose", &myStaticHandler1); assert(0); }
+ catch (MyEx ex) { assert(ex.option is null && ex.value is null); }
+
+ static void myStaticHandler2(string option) { throw new MyEx(option); }
+ args = ["program.name", "--verbose"];
+ try { getopt(args, "verbose", &myStaticHandler2); assert(0); }
+ catch (MyEx ex) { assert(ex.option == "verbose" && ex.value is null); }
+
+ static void myStaticHandler3(string option, string value) { throw new MyEx(option, value); }
+ args = ["program.name", "--verbose", "2"];
+ try { getopt(args, "verbose", &myStaticHandler3); assert(0); }
+ catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); }
+}
+
+@safe unittest // @safe std.getopt.config option use
+{
+ long x = 0;
+ string[] args = ["program", "--inc-x", "--inc-x"];
+ getopt(args,
+ std.getopt.config.caseSensitive,
+ "inc-x", "Add one to x", delegate void() { x++; });
+ assert(x == 2);
+}
+
+@system unittest
+{
+ // From bugzilla 2142
+ bool f_linenum, f_filename;
+ string[] args = [ "", "-nl" ];
+ getopt
+ (
+ args,
+ std.getopt.config.bundling,
+ //std.getopt.config.caseSensitive,
+ "linenum|l", &f_linenum,
+ "filename|n", &f_filename
+ );
+ assert(f_linenum);
+ assert(f_filename);
+}
+
+@system unittest
+{
+ // From bugzilla 6887
+ string[] p;
+ string[] args = ["", "-pa"];
+ getopt(args, "p", &p);
+ assert(p.length == 1);
+ assert(p[0] == "a");
+}
+
+@system unittest
+{
+ // From bugzilla 6888
+ int[string] foo;
+ auto args = ["", "-t", "a=1"];
+ getopt(args, "t", &foo);
+ assert(foo == ["a":1]);
+}
+
+@system unittest
+{
+ // From bugzilla 9583
+ int opt;
+ auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"];
+ getopt(args, "opt", &opt);
+ assert(args == ["prog", "--a", "--b", "--c"]);
+}
+
+@system unittest
+{
+ string foo, bar;
+ auto args = ["prog", "-thello", "-dbar=baz"];
+ getopt(args, "t", &foo, "d", &bar);
+ assert(foo == "hello");
+ assert(bar == "bar=baz");
+
+ // From bugzilla 5762
+ string a;
+ args = ["prog", "-a-0x12"];
+ getopt(args, config.bundling, "a|addr", &a);
+ assert(a == "-0x12", a);
+ args = ["prog", "--addr=-0x12"];
+ getopt(args, config.bundling, "a|addr", &a);
+ assert(a == "-0x12");
+
+ // From https://d.puremagic.com/issues/show_bug.cgi?id=11764
+ args = ["main", "-test"];
+ bool opt;
+ args.getopt(config.passThrough, "opt", &opt);
+ assert(args == ["main", "-test"]);
+
+ // From https://issues.dlang.org/show_bug.cgi?id=15220
+ args = ["main", "-o=str"];
+ string o;
+ args.getopt("o", &o);
+ assert(o == "str");
+
+ args = ["main", "-o=str"];
+ o = null;
+ args.getopt(config.bundling, "o", &o);
+ assert(o == "str");
+}
+
+@system unittest // 5228
+{
+ import std.conv;
+ import std.exception;
+
+ auto args = ["prog", "--foo=bar"];
+ int abc;
+ assertThrown!GetOptException(getopt(args, "abc", &abc));
+
+ args = ["prog", "--abc=string"];
+ assertThrown!ConvException(getopt(args, "abc", &abc));
+}
+
+@system unittest // From bugzilla 7693
+{
+ import std.exception;
+
+ enum Foo {
+ bar,
+ baz
+ }
+
+ auto args = ["prog", "--foo=barZZZ"];
+ Foo foo;
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo=bar"];
+ assertNotThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo", "barZZZ"];
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo", "baz"];
+ assertNotThrown(getopt(args, "foo", &foo));
+}
+
+@system unittest // same bug as 7693 only for bool
+{
+ import std.exception;
+
+ auto args = ["prog", "--foo=truefoobar"];
+ bool foo;
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo"];
+ getopt(args, "foo", &foo);
+ assert(foo);
+}
+
+@system unittest
+{
+ bool foo;
+ auto args = ["prog", "--foo"];
+ getopt(args, "foo", &foo);
+ assert(foo);
+}
+
+@system unittest
+{
+ bool foo;
+ bool bar;
+ auto args = ["prog", "--foo", "-b"];
+ getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo,
+ config.caseSensitive, "bar|b", "Some bar", &bar);
+ assert(foo);
+ assert(bar);
+}
+
+@system unittest
+{
+ bool foo;
+ bool bar;
+ auto args = ["prog", "-b", "--foo", "-z"];
+ getopt(args, config.caseInsensitive, config.required, "foo|f", "Some foo",
+ &foo, config.caseSensitive, "bar|b", "Some bar", &bar,
+ config.passThrough);
+ assert(foo);
+ assert(bar);
+}
+
+@system unittest
+{
+ import std.exception;
+
+ bool foo;
+ bool bar;
+ auto args = ["prog", "-b", "-z"];
+ assertThrown(getopt(args, config.caseInsensitive, config.required, "foo|f",
+ "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar,
+ config.passThrough));
+}
+
+@system unittest
+{
+ import std.exception;
+
+ bool foo;
+ bool bar;
+ auto args = ["prog", "--foo", "-z"];
+ assertNotThrown(getopt(args, config.caseInsensitive, config.required,
+ "foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar",
+ &bar, config.passThrough));
+ assert(foo);
+ assert(!bar);
+}
+
+@system unittest
+{
+ bool foo;
+ auto args = ["prog", "-f"];
+ auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo);
+ assert(foo);
+ assert(!r.helpWanted);
+}
+
+@safe unittest // implicit help option without config.passThrough
+{
+ string[] args = ["program", "--help"];
+ auto r = getopt(args);
+ assert(r.helpWanted);
+}
+
+// Issue 13316 - std.getopt: implicit help option breaks the next argument
+@system unittest
+{
+ string[] args = ["program", "--help", "--", "something"];
+ getopt(args);
+ assert(args == ["program", "something"]);
+
+ args = ["program", "--help", "--"];
+ getopt(args);
+ assert(args == ["program"]);
+
+ bool b;
+ args = ["program", "--help", "nonoption", "--option"];
+ getopt(args, config.stopOnFirstNonOption, "option", &b);
+ assert(args == ["program", "nonoption", "--option"]);
+}
+
+// Issue 13317 - std.getopt: endOfOptions broken when it doesn't look like an option
+@system unittest
+{
+ auto endOfOptionsBackup = endOfOptions;
+ scope(exit) endOfOptions = endOfOptionsBackup;
+ endOfOptions = "endofoptions";
+ string[] args = ["program", "endofoptions", "--option"];
+ bool b = false;
+ getopt(args, "option", &b);
+ assert(!b);
+ assert(args == ["program", "--option"]);
+}
+
+/** This function prints the passed $(D Option)s and text in an aligned manner on $(D stdout).
+
+The passed text will be printed first, followed by a newline, then the short
+and long version of every option will be printed. The short and long version
+will be aligned to the longest option of every $(D Option) passed. If the option
+is required, then "Required:" will be printed after the long version of the
+$(D Option). If a help message is present it will be printed next. The format is
+illustrated by this code:
+
+------------
+foreach (it; opt)
+{
+ writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
+ lengthOfLongestLongOption, it.optLong,
+ it.required ? " Required: " : " ", it.help);
+}
+------------
+
+Params:
+ text = The text to printed at the beginning of the help output.
+ opt = The $(D Option) extracted from the $(D getopt) parameter.
+*/
+void defaultGetoptPrinter(string text, Option[] opt)
+{
+ import std.stdio : stdout;
+
+ defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt);
+}
+
+/** This function writes the passed text and $(D Option) into an output range
+in the manner described in the documentation of function
+$(D defaultGetoptPrinter).
+
+Params:
+ output = The output range used to write the help information.
+ text = The text to print at the beginning of the help output.
+ opt = The $(D Option) extracted from the $(D getopt) parameter.
+*/
+void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
+{
+ import std.algorithm.comparison : min, max;
+ import std.format : formattedWrite;
+
+ output.formattedWrite("%s\n", text);
+
+ size_t ls, ll;
+ bool hasRequired = false;
+ foreach (it; opt)
+ {
+ ls = max(ls, it.optShort.length);
+ ll = max(ll, it.optLong.length);
+
+ hasRequired = hasRequired || it.required;
+ }
+
+ string re = " Required: ";
+
+ foreach (it; opt)
+ {
+ output.formattedWrite("%*s %*s%*s%s\n", ls, it.optShort, ll, it.optLong,
+ hasRequired ? re.length : 1, it.required ? re : " ", it.help);
+ }
+}
+
+@system unittest
+{
+ import std.conv;
+
+ import std.array;
+ import std.string;
+ bool a;
+ auto args = ["prog", "--foo"];
+ auto t = getopt(args, "foo|f", "Help", &a);
+ string s;
+ auto app = appender!string();
+ defaultGetoptFormatter(app, "Some Text", t.options);
+
+ string helpMsg = app.data;
+ //writeln(helpMsg);
+ assert(helpMsg.length);
+ assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
+ ~ helpMsg);
+ assert(helpMsg.indexOf("--foo") != -1);
+ assert(helpMsg.indexOf("-f") != -1);
+ assert(helpMsg.indexOf("-h") != -1);
+ assert(helpMsg.indexOf("--help") != -1);
+ assert(helpMsg.indexOf("Help") != -1);
+
+ string wanted = "Some Text\n-f --foo Help\n-h --help This help "
+ ~ "information.\n";
+ assert(wanted == helpMsg);
+}
+
+@system unittest
+{
+ import std.array ;
+ import std.conv;
+ import std.string;
+ bool a;
+ auto args = ["prog", "--foo"];
+ auto t = getopt(args, config.required, "foo|f", "Help", &a);
+ string s;
+ auto app = appender!string();
+ defaultGetoptFormatter(app, "Some Text", t.options);
+
+ string helpMsg = app.data;
+ //writeln(helpMsg);
+ assert(helpMsg.length);
+ assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
+ ~ helpMsg);
+ assert(helpMsg.indexOf("Required:") != -1);
+ assert(helpMsg.indexOf("--foo") != -1);
+ assert(helpMsg.indexOf("-f") != -1);
+ assert(helpMsg.indexOf("-h") != -1);
+ assert(helpMsg.indexOf("--help") != -1);
+ assert(helpMsg.indexOf("Help") != -1);
+
+ string wanted = "Some Text\n-f --foo Required: Help\n-h --help "
+ ~ " This help information.\n";
+ assert(wanted == helpMsg, helpMsg ~ wanted);
+}
+
+@system unittest // Issue 14724
+{
+ bool a;
+ auto args = ["prog", "--help"];
+ GetoptResult rslt;
+ try
+ {
+ rslt = getopt(args, config.required, "foo|f", "bool a", &a);
+ }
+ catch (Exception e)
+ {
+ enum errorMsg = "If the request for help was passed required options" ~
+ "must not be set.";
+ assert(false, errorMsg);
+ }
+
+ assert(rslt.helpWanted);
+}
+
+// throw on duplicate options
+@system unittest
+{
+ import core.exception;
+ auto args = ["prog", "--abc", "1"];
+ int abc, def;
+ assertThrown!AssertError(getopt(args, "abc", &abc, "abc", &abc));
+ assertThrown!AssertError(getopt(args, "abc|a", &abc, "def|a", &def));
+ assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def));
+}
+
+@system unittest // Issue 17327 repeated option use
+{
+ long num = 0;
+
+ string[] args = ["program", "--num", "3"];
+ getopt(args, "n|num", &num);
+ assert(num == 3);
+
+ args = ["program", "--num", "3", "--num", "5"];
+ getopt(args, "n|num", &num);
+ assert(num == 5);
+
+ args = ["program", "--n", "3", "--num", "5", "-n", "-7"];
+ getopt(args, "n|num", &num);
+ assert(num == -7);
+
+ void add1() { num++; }
+ void add2(string option) { num += 2; }
+ void addN(string option, string value)
+ {
+ import std.conv : to;
+ num += value.to!long;
+ }
+
+ num = 0;
+ args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
+ getopt(args,
+ "add1", "Add 1 to num", &add1,
+ "add2", "Add 2 to num", &add2,
+ "add", "Add N to num", &addN,);
+ assert(num == 21);
+
+ bool flag = false;
+ args = ["program", "--flag"];
+ getopt(args, "f|flag", "Boolean", &flag);
+ assert(flag);
+
+ flag = false;
+ args = ["program", "-f", "-f"];
+ getopt(args, "f|flag", "Boolean", &flag);
+ assert(flag);
+
+ flag = false;
+ args = ["program", "--flag=true", "--flag=false"];
+ getopt(args, "f|flag", "Boolean", &flag);
+ assert(!flag);
+
+ flag = false;
+ args = ["program", "--flag=true", "--flag=false", "-f"];
+ getopt(args, "f|flag", "Boolean", &flag);
+ assert(flag);
+}
+
+@safe unittest // Delegates as callbacks
+{
+ alias TwoArgOptionHandler = void delegate(string option, string value) @safe;
+
+ TwoArgOptionHandler makeAddNHandler(ref long dest)
+ {
+ void addN(ref long dest, string n)
+ {
+ import std.conv : to;
+ dest += n.to!long;
+ }
+
+ return (option, value) => addN(dest, value);
+ }
+
+ long x = 0;
+ long y = 0;
+
+ string[] args =
+ ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10",
+ "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"];
+
+ getopt(args,
+ "x-plus-1", "Add one to x", delegate void() { x += 1; },
+ "x-plus-5", "Add five to x", delegate void(string option) { x += 5; },
+ "x-plus-n", "Add NUM to x", makeAddNHandler(x),
+ "y-plus-7", "Add seven to y", delegate void() { y += 7; },
+ "y-plus-3", "Add three to y", delegate void(string option) { y += 3; },
+ "y-plus-n", "Add NUM to x", makeAddNHandler(y),);
+
+ assert(x == 17);
+ assert(y == 50);
+}
+
+@system unittest // Hyphens at the start of option values; Issue 17650
+{
+ auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
+
+ int m;
+ int n;
+ char c;
+ string f;
+
+ getopt(args,
+ "m|mm", "integer", &m,
+ "n|nn", "integer", &n,
+ "c|cc", "character", &c,
+ "f|file", "filename or hyphen for stdin", &f);
+
+ assert(m == -5);
+ assert(n == -50);
+ assert(c == '-');
+ assert(f == "-");
+}