1 /*******************************************************************************
2 
3     Module to manage command-line arguments.
4 
5     ____________________________________________________________________________
6 
7     Simple usage:
8 
9     ---
10 
11         int main ( istring[] cl_args )
12         {
13             // Create an object to parse command-line arguments
14             auto args = new Arguments;
15 
16             // Setup what arguments are valid
17             // (these can be configured in various ways as will be demonstrated
18             // later in the documentation)
19             args("alpha");
20             args("bravo");
21 
22             // Parse the actual command-line arguments given to the application
23             // (the first element is the application name, so that should not be
24             // passed to the 'parse()' function)
25             auto args_ok = args.parse(cl_args[1 .. $]);
26 
27             if ( args_ok )
28             {
29                 // Proceed with rest of the application
30                 ...
31             }
32             else
33             {
34                 // Discover what caused the error and handle appropriately
35             }
36         }
37 
38     ---
39 
40     ____________________________________________________________________________
41 
42 
43     For the sake of brevity, the rest of this documentation will not show the
44     'main()' function or the creation of the 'args' object. Also, setting up of
45     arguments will be shown only where necessary. Moreover, the 'args.parse()'
46     function will be called with a custom string representing the command-line
47     arguments. This is as shown in the following example:
48 
49     ---
50 
51         args.parse("--alpha --bravo");
52 
53         if ( args("alpha").set )
54         {
55             // This will be reached as '--alpha' was given
56         }
57 
58         if ( args("bravo").set )
59         {
60             // This will be reached as '--bravo' was given
61         }
62 
63         if ( args("charlie").set )
64         {
65             // This will *not* be reached as '--charlie' was not given
66         }
67 
68     ---
69 
70     ____________________________________________________________________________
71 
72 
73     When arguments are being set up, normally all arguments that an application
74     supports are explicitly declared and suitably configured. But sometimes, it
75     may be desirable to use on-the-fly arguments that are not set up but
76     discovered during parsing. Such arguments are called 'sloppy arguments'.
77     Support for sloppy arguments is disabled by default, but can be enabled when
78     calling the 'parse()' function, as shown below:
79 
80     ---
81 
82         args("alpha");
83 
84         args.parse("--alpha --bravo");
85             // This will result in an error because only 'alpha' was declared,
86             // but not 'bravo'.
87 
88         args.parse("--alpha --bravo", true);
89             // This, on the other hand would work. Space for 'bravo' (and
90             // potentially any of its parameters) would be allocated when
91             // 'bravo' gets discovered during parsing.
92 
93     ---
94 
95     ____________________________________________________________________________
96 
97 
98     Arguments can be configured to have aliases. This is a convenient way to
99     represent arguments with long names. Aliases are always exactly one
100     character long. An argument can have multiple aliases. Aliases are always
101     given on the command-line using the short prefix.
102 
103     ---
104 
105         args("alpha").aliased('a');
106         args("help").aliased('?').aliased('h'); // multiple aliases allowed
107 
108         args.parse("-a -?");
109 
110     ---
111 
112     ____________________________________________________________________________
113 
114 
115     Arguments can be configured to be mandatorily present, by calling the
116     'required()' function as follows:
117 
118     ---
119 
120         args("alpha").required();
121 
122         args.parse("--bravo");
123             // This will fail because the required argument 'alpha' was not
124             // given.
125 
126     ---
127 
128     ____________________________________________________________________________
129 
130 
131     An argument can be configured to depend upon another, by calling the
132     'requires()' function as follows:
133 
134     ---
135 
136         args("alpha");
137         args("bravo").requires("alpha");
138 
139         args.parse("--bravo");
140             // This will fail because 'bravo' needs 'alpha', but 'alpha' was not
141             // given.
142 
143         args.parse("--alpha --bravo");
144             // This, on the other hand, will succeed.
145 
146     ---
147 
148     ____________________________________________________________________________
149 
150 
151     An argument can be configured to conflict with another, by calling the
152     'conflicts()' function as follows:
153 
154     ---
155 
156         args("alpha");
157         args("bravo").conflicts("alpha");
158 
159         args.parse("--alpha --bravo");
160             // This will fail because 'bravo' conflicts with 'alpha', so both of
161             // them can't be present together.
162 
163     ---
164 
165     ____________________________________________________________________________
166 
167 
168     By default arguments don't have any associated parameters. When setting up
169     arguments, they can be configured to have zero or more associated
170     parameters. Parameters assigned to an argument can be accessed using that
171     argument's 'assigned[]' array at consecutive indices. The number of
172     parameters assigned to an argument must exactly match the number of
173     parameters it has been set up to have, or else parsing will fail. Dealing
174     with parameters is shown in the following example:
175 
176     ---
177 
178         args("alpha");
179         args("bravo").params(0);
180             // Doing `params(0)` is redundant
181         args("charlie").params(1);
182             // 'charlie' must have exactly one associated parameter
183 
184         args.parse("--alpha --bravo --charlie=chaplin");
185             // the parameter assigned to 'charlie' (i.e. 'chaplin') can be
186             // accessed using `args("charlie").assigned[0]`
187 
188     ---
189 
190     ____________________________________________________________________________
191 
192 
193     Parameter assignment can be either explicit or implicit. Explicit assignment
194     is done using an assignment symbol (defaults to '=', can be changed),
195     whereas implicit assignment happens when a parameter is found after a
196     whitespace.
197     Implicit assignment always happens to the last known argument target, such
198     that multiple parameters accumulate (until the configured parameters count
199     for that argument is reached). Any extra parameters encountered after that
200     are assigned to a special 'null' argument. The 'null' argument is always
201     defined and acts as an accumulator for parameters left uncaptured by other
202     arguments.
203 
204     Please note:
205         * if sloppy arguments are supported, and if a sloppy argument happens to
206           be the last known argument target, then implicit assignment of any
207           extra parameters will happen to that sloppy argument.
208           [example 2 below]
209 
210         * explicit assignment to an argument always associates the parameter
211           with that argument even if that argument's parameters count has been
212           reached. In this case, 'parse()' will fail.
213           [example 3 below]
214 
215     ---
216 
217         args("alpha").params(3);
218 
219         // Example 1
220         args.parse("--alpha=one --alpha=two three four");
221             // In this case, 'alpha' would have 3 parameters assigned to it (so
222             // its 'assigned' array would be `["one", "two", "three"]`), and the
223             // null argument would have 1 parameter (with its 'assigned' array
224             // being `["four"]`).
225             // Here's why:
226             // Two of these parameters ('one' & 'two') were assigned explicitly.
227             // The next parameter ('three') was assigned implicitly since
228             // 'alpha' was the last known argument target. At this point,
229             // alpha's parameters count is reached, so no more implicit
230             // assignment will happen to 'alpha'.
231             // So the last parameter ('four') is assigned to the special 'null'
232             // argument.
233 
234         // Example 2
235         // (sloppy arguments supported by passing 'true' as the second parameter
236         // to 'parse()')
237         args.parse("--alpha one two three four --xray five six", true);
238             // In this case, 'alpha' would get its 3 parameters ('one', 'two' &
239             // 'three') by way of implicit assignment.
240             // Parameter 'four' would be assigned to the 'null' argument (since
241             // implicit assignment to the last known argument target 'alpha' is
242             // not possible as alpha's parameter count has been reached).
243             // The sloppy argument 'xray' now becomes the new last known
244             // argument target and hence gets the last two parameters ('five' &
245             // 'six').
246 
247         // Example 3
248         args.parse("--alpha one two three --alpha=four");
249             // As before, 'alpha' would get its 3 parameters ('one', 'two' &
250             // 'three') by way of implicit assignment.
251             // Since 'four' is being explicitly assigned to 'alpha', parsing
252             // will fail here as 'alpha' has been configured to have at most 3
253             // parameters.
254 
255     ---
256 
257     ____________________________________________________________________________
258 
259 
260     An argument can be configured to have one or more default parameters. This
261     means that if the argument was not given on the command-line, it would still
262     contain the configured parameter(s).
263     It is, of course, possible to have no default parameters configured. But if
264     one or more default parameters have been configured, then their number must
265     exactly match the number of parameters configured.
266 
267     Please note:
268         * Irrespective of whether default parameters have been configured or not,
269           if an argument was not given on the command-line, its 'set()' function
270           would return 'false'.
271           [example 1 below]
272 
273         * Irrespective of whether default parameters have been configured or not,
274           if an argument is given on the command-line, it must honour its
275           configured number of parameters.
276           [example 2 below]
277 
278     ---
279 
280         args("alpha").params(1).defaults("one");
281 
282         // Example 1
283         args.parse("--bravo");
284             // 'alpha' was not given, so `args("alpha").set` would return false
285             // but still `args("alpha").assigned[0]` would contain 'one'
286 
287         // Example 2
288         args.parse("--alpha");
289             // this will fail because 'alpha' expects a parameter and that was
290             // not given. In this case, the configured default parameter will
291             // *not* be picked up.
292 
293     ---
294 
295     ____________________________________________________________________________
296 
297 
298     Parameters of an argument can be restricted to a pre-defined set of
299     acceptable values. In this case, argument parsing will fail on an attempt to
300     assign a value from outside the set:
301 
302     ---
303 
304         args("greeting").restrict(["hello", "namaste", "ahoj", "hola"]);
305         args("enabled").restrict(["true", "false", "t", "f", "y", "n"]);
306 
307         args.parse("--greeting=bye");
308             // This will fail since 'bye' is not among the acceptable values
309 
310     ---
311 
312     ____________________________________________________________________________
313 
314 
315     The parser makes a distinction between long prefix arguments and short
316     prefix arguments. Long prefix arguments start with two hyphens (--argument),
317     while short prefix arguments start with a single hyphen (-a) [the prefixes
318     themselves are configurable, as shown in later documentation]. Within a
319     short prefix argument, each character represents an individual argument.
320     Long prefix arguments must always be distinct, while short prefix arguments
321     may be combined together.
322 
323     ---
324 
325         args.parse("--alpha -b");
326             // The argument 'alpha' will be set.
327             // The argument represented by 'b' will be set (note that 'b' here
328             // could be an alias to another argument, or could be the argument
329             // name itself)
330 
331     ---
332 
333     ____________________________________________________________________________
334 
335 
336     When assigning parameters to an argument using the argument's short prefix
337     version, it is possible to "smush" the parameter with the argument. Smushing
338     refers to omitting the explicit assignment symbol ('=' by default) or
339     whitespace (when relying on implicit assignment) that separates an argument
340     from its parameter. The ability to smush an argument with its parameter in
341     this manner has to be explicitly enabled using the 'smush()' function.
342 
343     Please note:
344         * smushing cannot be done with the long prefix version of an argument
345           [example 2 below]
346 
347         * smushing is irrelevant if an argument has no parameters
348           [example 3 below]
349 
350         * if an argument has more than one parameter, and smushing is desired,
351           then the short prefix version of the argument needs to be repeated as
352           many times as the number of parameters to be assigned (this is because
353           one smush can only assign one parameter at a time)
354           [example 4 below]
355 
356         * smushing cannot be used if the parameter contains the explicit
357           assignment symbol ('=' by default). In this case, either explicit or
358           implicit assignment should be used. This limitation is due to how
359           argv/argc values are stripped of original quotes.
360           [example 5 below]
361 
362     ---
363 
364         // Example 1
365         args("alpha").aliased('a').params(1).smush;
366         args.parse("-aparam");
367             // OK - this is equivalent to `args.parse("-a param");`
368 
369         // Example 2
370         args("bravo").params(1).smush;
371         args.parse("--bravoparam");
372             // ERROR - 'param' cannot be smushed with 'bravo'
373 
374         // Example 3
375         args("charlie").smush;
376             // irrelevant smush as argument has no parameters
377 
378         // Example 4
379         args('d').params(2).smush;
380         args.parse("-dfile1 -dfile2");
381             // smushing multiple parameters requires the short prefix version of
382             // the argument to be repeated. This could have been done without
383             // smushing as `args.parse("-d file1 file2);`
384 
385         // Example 5
386         args("e").params(1).smush;
387         args.parse("-e'foo=bar'");
388             // The parameter 'foo=bar' cannot be smushed with the argument as
389             // the parameter contains '=' within. Be especially careful of this
390             // as the 'parse()' function will not fail in this case, but may
391             // result in unexpected behaviour.
392             // The proper way to assign a parameter containing the explicit
393             // assignment symbol is to use one of the following:
394             //     args.parse("-e='foo=bar'"); // explicit assignment
395             //     args.parse("-e 'foo=bar'"); // implicit assignment
396 
397     ---
398 
399     ____________________________________________________________________________
400 
401 
402     The prefixes used for the long prefix and the short prefix version of the
403     arguments default to '--' & '-' respectively, but they are configurable. To
404     change these, the desired prefix strings need to be passed to the
405     constructor as shown below:
406 
407     ---
408 
409         // Change short prefix to '/' & long prefix to '%'
410         auto args = new Arguments(null, null, null, null, "/", "%");
411 
412         args.parse("%alpha=param %bravo /abc");
413             // arguments 'alpha' & 'bravo' set using the long prefix version
414             // arguments represented by the characters 'a', 'b' & 'c' set using
415             // the short prefix version
416 
417     ---
418 
419     Note that it is also possible to disable both prefixes by passing 'null' as
420     the constructor parameters.
421 
422     ____________________________________________________________________________
423 
424 
425     We noted in the documentation earlier that a parameter following a
426     whitespace gets assigned to the last known target (implicit assignment). On
427     the other hand, the symbol used for explicitly assigning a parameter to an
428     argument defaults to '='. This symbol is also configurable, and can be
429     changed by passing the desired symbol character to the constructor as
430     shown below:
431 
432     ---
433 
434         // Change the parameter assignment symbol to ':'
435         // (the short prefix and long prefix need to be passed as their default
436         // values since we're not changing them)
437         auto args = new Arguments(null, null, null, null, "-", "--", ':');
438 
439         args.parse("--alpha:param");
440             // argument 'alpha' will be assigned parameter 'param' using
441             // explicit assignment
442 
443     ---
444 
445     ____________________________________________________________________________
446 
447 
448     All text following a "--" token are treated as parameters (even if they
449     start with the long prefix or the short prefix). This notion is applied by
450     unix systems to terminate argument processing in a similar manner.
451 
452     These parameters are always assigned to the special 'null' argument.
453 
454     ---
455 
456         args("alpha").params(1);
457 
458         args.parse("--alpha one -- -two --three");
459             // 'alpha' gets one parameter ('one')
460             // the null argument gets two parameters ('-two' & '--three')
461             // note how 'two' & 'three' are prefixed by the short and long
462             // prefixes respectively, but the prefixes don't play any part as
463             // these are just parameters now
464 
465     ---
466 
467     ____________________________________________________________________________
468 
469 
470     When configuring the command-line arguments, qualifiers can be chained
471     together as shown in the following example:
472 
473     ---
474 
475         args("alpha")
476             .required
477             .params(1)
478             .aliased('a')
479             .requires("bravo")
480             .conflicts("charlie")
481             .defaults("one");
482 
483     ---
484 
485     ____________________________________________________________________________
486 
487     The full help message for the application (which includes the configured
488     usage, long & short descriptions as well as the help text of each of the
489     arguments) can be displayed using the 'displayHelp()' function as follows:
490 
491     ---
492 
493         auto args = new Arguments(
494             "my_app",
495             "{0} : this is a short description",
496             "this is the usage string",
497             "this is a long description on how to make '{0}' work");
498 
499         args("alpha")
500             .aliased('a')
501             .params(1,3)
502             .help("help for alpha");
503         args("bravo")
504             .aliased('b')
505             .params(1)
506             .defaults("val")
507             .help("help for bravo");
508 
509         args.displayHelp();
510 
511     ---
512 
513     Doing this, would produce the following help message:
514 
515         my_app : this is a short description
516 
517         Usage:  this is the usage string
518 
519         this is a long description on how to make 'my_app' work
520 
521         Program options:
522           -a, --alpha  help for alpha (1-3 params)
523           -b, --bravo  help for bravo (1 param, default: [val])
524 
525     ____________________________________________________________________________
526 
527 
528     The 'parse()' function will return true only where all conditions are met.
529     If an error occurs, the parser will set an error code and return false.
530 
531     The error codes (which indicate the nature of the error) are as follows:
532 
533         None     : ok (no error)
534         ParamLo  : too few parameters were assigned to this argument
535         ParamHi  : too many parameters were assigned to this argument
536         Required : this is a required argument, but was not given
537         Requires : this argument depends on another argument which was not given
538         Conflict : this argument conflicts with another given argument
539         Extra    : unexpected argument (will not trigger an error if sloppy
540                    arguments are enabled)
541         Option   : parameter assigned is not one of the acceptable options
542 
543 
544     A simple way to handle errors is to invoke an internal format routine, which
545     constructs error messages on your behalf. The messages are constructed using
546     a layout handler and the messages themselves may be customized (for i18n
547     purposes). See the two 'errors()' methods for more information on this. The
548     following example shows this way of handling errors:
549 
550     ---
551 
552         if ( ! args.parse (...) )
553         {
554             stderr(args.errors(&stderr.layout.sprint));
555         }
556 
557     ---
558 
559 
560     Another way of handling argument parsing errors, is to traverse the set of
561     arguments, to find out exactly which argument has the error, and what is the
562     error code. This is as shown in the following example:
563 
564     ---
565 
566         if ( ! args.parse (...) )
567         {
568             foreach ( arg; args )
569             {
570                 if ( arg.error )
571                 {
572                     // 'arg.error' contains one of the above error-codes
573 
574                     ...
575                 }
576             }
577         }
578 
579     ---
580 
581     ____________________________________________________________________________
582 
583 
584     The following two types of callbacks are supported:
585         - a callback called when an argument is parsed
586         - a callback called whenever a parameter gets assigned to an argument
587     (see the 'bind()' methods for the signatures of these delegates).
588 
589     ____________________________________________________________________________
590 
591     Copyright:
592         Copyright (c) 2009 Kris.
593         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
594         All rights reserved.
595 
596     License:
597         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
598         See LICENSE_TANGO.txt for details.
599 
600 *******************************************************************************/
601 
602 module ocean.text.Arguments;
603 
604 
605 
606 
607 import ocean.meta.types.Qualifiers;
608 
609 import ocean.io.Stdout;
610 import ocean.io.stream.Format : FormatOutput;
611 import ocean.math.Math;
612 import ocean.text.Util;
613 import ocean.text.convert.Formatter;
614 import ocean.text.convert.Integer;
615 import ocean.util.container.SortedMap;
616 import ocean.util.container.more.Stack;
617 import ocean.core.TypeConvert: assumeUnique;
618 import ocean.core.Verify;
619 
620 
621 version (unittest) import ocean.core.Test;
622 
623 /*******************************************************************************
624 
625     The main arguments container class.
626 
627 *******************************************************************************/
628 
629 public class Arguments
630 {
631     import ocean.core.Enforce : enforce;
632 
633     /***************************************************************************
634 
635         Convenience aliases to access a specific argument instance
636 
637     ***************************************************************************/
638 
639     public alias get opCall;  // args("name")
640     public alias get opIndex; // args["name"]
641 
642 
643     /***************************************************************************
644 
645         Convenience alias to get the value of a boolean argument
646 
647     ***************************************************************************/
648 
649     public alias getBool exists;
650 
651 
652     /***************************************************************************
653 
654         Application's name to use in help messages.
655 
656     ***************************************************************************/
657 
658     public istring app_name;
659 
660 
661     /***************************************************************************
662 
663         Application's short usage description (as a format string).
664 
665         This is used as a format string to print the usage. The first parameter
666         to the format string is the application's name. This string should
667         describe how to invoke the application.
668 
669         If the usage description spans multiple lines, then it's better to start
670         each line with a tab character (\t).
671 
672         Examples:
673 
674         ---
675 
676             args.usage = "{0} [OPTIONS] SOMETHING FILE";
677             args.usage = "{0} [OPTIONS] SOMETHING FILE\n"
678                          "\t{0} --version";
679 
680         ---
681 
682     ***************************************************************************/
683 
684     public istring usage = "{0} [OPTIONS] [ARGS]";
685 
686 
687     /***************************************************************************
688 
689         One line description of what the application does (as a format string).
690 
691         This is used as a format string to print a short description of what the
692         application does. The first argument is the name of the application (but
693         the name shouldn't normally be used in the description).
694 
695     ***************************************************************************/
696 
697     public istring short_desc;
698 
699 
700     /***************************************************************************
701 
702         Long description about the application and how to use it (as a format
703         string).
704 
705         This is used as a format string to print a long description of what the
706         application does and how to use it. The first argument is the name of
707         the application.
708 
709     ***************************************************************************/
710 
711     public istring long_desc;
712 
713 
714     /***************************************************************************
715 
716         Stack used to help in assigning implicitly assigned parameters to
717         arguments during parsing.
718 
719         This stack contains argument instances of only those arguments that can
720         have one or more associated parameters. Implicit parameters always get
721         assigned to the topmost argument in the stack. Once the number of
722         parameters of the topmost argument in the stack reaches its maximum
723         configured value, that argument gets popped off the stack. Future
724         implicit assignments will then happen to the new topmost argument in the
725         stack.
726 
727         The null argument is always the first one that gets pushed onto the
728         stack. This ensures that it is able to "catch" all unclaimed parameters
729         at the end.
730 
731     ***************************************************************************/
732 
733     private Stack!(Argument) stack;
734 
735 
736     /***************************************************************************
737 
738         All argument instances. A sorted map (indexed by the argument name) is
739         used to store these so that the arguments appear in a sorted manner in
740         the help text output
741 
742     ***************************************************************************/
743 
744     private SortedMap!(cstring, Argument) args;
745 
746 
747     /***************************************************************************
748 
749         Argument instances that have aliases. A sorted map (indexed by the
750         argument aliases) is used to store these so that the arguments appear in
751         a sorted manner in the help text output
752 
753     ***************************************************************************/
754 
755     private SortedMap!(cstring, Argument) aliases;
756 
757 
758     /***************************************************************************
759 
760         Character to be used as the explicit assignment symbol
761 
762     ***************************************************************************/
763 
764     private char eq;
765 
766 
767     /***************************************************************************
768 
769         The short prefix string
770 
771     ***************************************************************************/
772 
773     private istring sp;
774 
775 
776     /***************************************************************************
777 
778         The long prefix string
779 
780     ***************************************************************************/
781 
782     private istring lp;
783 
784 
785     /***************************************************************************
786 
787         Error messages
788 
789     ***************************************************************************/
790 
791     private const(istring)[] msgs;
792 
793 
794     /***************************************************************************
795 
796         Format strings of all default errors
797 
798     ***************************************************************************/
799 
800     private static immutable istring[] errmsg = [
801         "argument '{0}' expects {2} parameter(s) but has {1}\n",
802         "argument '{0}' expects {3} parameter(s) but has {1}\n",
803         "argument '{0}' is missing\n",
804         "argument '{0}' requires '{4}'\n",
805         "argument '{0}' conflicts with '{4}'\n",
806         "unexpected argument '{0}'\n",
807         "argument '{0}' expects one of {5}\n",
808         "invalid parameter for argument '{0}': {4}\n",
809     ];
810 
811 
812     /***************************************************************************
813 
814         Internal string used for spacing of the full help message
815 
816     ***************************************************************************/
817 
818     private mstring spaces;
819 
820 
821     /***************************************************************************
822 
823         Temporary formatting buffer
824 
825     ***************************************************************************/
826 
827     private mstring tmp_buf;
828 
829 
830     /***************************************************************************
831 
832         Maximum width of the column showing argument aliases in the full help
833         message
834 
835     ***************************************************************************/
836 
837     private size_t aliases_width;
838 
839 
840     /***************************************************************************
841 
842         Maximum width of the column showing argument names in the full help
843         message
844 
845     ***************************************************************************/
846 
847     private size_t long_name_width;
848 
849 
850     /***************************************************************************
851 
852         Constructor.
853 
854         Params:
855             app_name = name of the application (to show in the help message)
856             short_desc = short description of what the application does (should
857                 be one line only, preferably less than 80 characters long)
858             usage = how the application is supposed to be invoked
859             long_desc = long description of what the application does and how to
860                 use it
861             sp = string to use as the short prefix (defaults to '-')
862             lp = string to use as the long prefix (defaults to '--')
863             eq = character to use as the explicit assignment symbol
864                  (defaults to '=')
865 
866     ***************************************************************************/
867 
868     public this ( istring app_name = null, istring short_desc = null,
869         istring usage = null, istring long_desc = null, istring sp = "-",
870         istring lp = "--", char eq = '=' )
871     {
872         this.msgs = this.errmsg;
873 
874         this.app_name = app_name;
875         this.short_desc = short_desc;
876         this.long_desc = long_desc;
877         this.sp = sp;
878         this.lp = lp;
879         this.eq = eq;
880 
881         this.args = new typeof(this.args)();
882         this.aliases = new typeof(this.aliases)();
883 
884         if ( usage.length > 0 )
885         {
886             this.usage = usage;
887         }
888 
889         this.get(null).params; // set null argument to consume params
890     }
891 
892 
893     /***************************************************************************
894 
895         Parses the command-line arguments into a set of Argument instances. The
896         command-line arguments are expected to be passed in a string.
897 
898         Params:
899             input = string to be parsed (contains command-line arguments)
900             sloppy = true if any unexpected arguments found during parsing
901                 should be accepted on-the-fly, false if unexpected arguments
902                 should be treated as error
903 
904         Returns:
905             true if parsing was successful, false otherwise
906 
907     ***************************************************************************/
908 
909     public bool parse ( istring input, bool sloppy = false )
910     {
911         istring[] tmp;
912 
913         foreach ( s; quotes(input, " ") )
914         {
915             tmp ~= s;
916         }
917 
918         return parse(tmp, sloppy);
919     }
920 
921 
922     /***************************************************************************
923 
924         Parses the command-line arguments into a set of Argument instances. The
925         command-line arguments are expected to be passed in an array of strings.
926 
927         Params:
928             input = array of strings to be parsed (contains command-line
929                 arguments)
930             sloppy = true if any unexpected arguments found during parsing
931                 should be accepted on-the-fly, false if unexpected arguments
932                 should be treated as error
933 
934         Returns:
935             true if parsing was successful, false otherwise
936 
937     ***************************************************************************/
938 
939     public bool parse ( const(istring)[] input, bool sloppy = false )
940     {
941         bool done;
942         int error;
943 
944         stack.push(this.get(null));
945 
946         foreach ( s; input )
947         {
948             if ( done is false )
949             {
950                 if ( s == "--" )
951                 {
952                     done = true;
953 
954                     stack.clear.push(this.get(null));
955 
956                     continue;
957                 }
958                 else
959                 {
960                     if ( argument(s, lp, sloppy, false) ||
961                          argument(s, sp, sloppy, true) )
962                     {
963                         continue;
964                     }
965                 }
966             }
967 
968             stack.top.append (s);
969         }
970 
971         foreach ( arg; args )
972         {
973             error |= arg.valid;
974         }
975 
976         return error is 0;
977     }
978 
979 
980     /***************************************************************************
981 
982         Unsets all configured arguments (as if they weren't given at all on the
983         command-line), clears all parameters that may have been assigned to
984         arguments and also clears any parsing errors that may have been
985         associated with any argument(s).
986 
987         Note that configured arguments are *not* removed.
988 
989         Returns:
990             this object for method chaining
991 
992     ***************************************************************************/
993 
994     public Arguments clear ( )
995     {
996         stack.clear;
997 
998         foreach ( arg; args )
999         {
1000             arg.set = false;
1001             arg.values = null;
1002             arg.error = arg.None;
1003         }
1004 
1005         return this;
1006     }
1007 
1008 
1009     /***************************************************************************
1010 
1011         Gets a reference to an argument, creating a new instance if necessary.
1012 
1013         Params:
1014             name = character representing the argument to be retrieved (this is
1015                 usually an alias to the argument, but could also be the argument
1016                 name if the argument name is exactly one character long)
1017 
1018         Returns:
1019             a reference to the argument
1020 
1021     ***************************************************************************/
1022 
1023     public Argument get ( char name )
1024     {
1025         return get(cast(cstring)(&name)[0 .. 1]);
1026     }
1027 
1028 
1029     /***************************************************************************
1030 
1031         Gets a reference to an argument, creating a new instance if necessary.
1032 
1033         Params:
1034             name = string containing the argument name (pass null to access the
1035                 special 'null' argument)
1036 
1037         Returns:
1038             a reference to the argument
1039 
1040     ***************************************************************************/
1041 
1042     public Argument get ( cstring name )
1043     {
1044         auto a = name in args;
1045 
1046         if ( a is null )
1047         {
1048             auto _name = idup(name);
1049 
1050             auto arg = new Argument(_name);
1051 
1052             args[_name] = arg;
1053 
1054             return arg;
1055         }
1056 
1057         return *a;
1058     }
1059 
1060 
1061     /***************************************************************************
1062 
1063         Enables 'foreach' iteration over the set of configured arguments.
1064 
1065         Params:
1066             dg = delegate called for each argument
1067 
1068     ***************************************************************************/
1069 
1070     public int opApply ( scope int delegate(ref Argument) dg )
1071     {
1072         int result;
1073 
1074         foreach ( arg; args )
1075         {
1076             if ( (result = dg(arg)) != 0 )
1077             {
1078                 break;
1079             }
1080         }
1081 
1082         return result;
1083     }
1084 
1085     /***************************************************************************
1086 
1087         Constructs a string of error messages.
1088 
1089         Returns:
1090             formatted error message string
1091 
1092     ***************************************************************************/
1093 
1094     public istring errors ()
1095     {
1096         mstring result;
1097 
1098         foreach (arg; args)
1099         {
1100             if (arg.error)
1101             {
1102                 sformat(
1103                     result, msgs[arg.error-1], arg.name,
1104                     arg.values.length, arg.min, arg.max, arg.bogus,
1105                     arg.options);
1106             }
1107         }
1108 
1109         return assumeUnique(result);
1110     }
1111 
1112 
1113     /***************************************************************************
1114 
1115         Replaces the default error messages with the given string.
1116         Note that arguments are passed to the formatter in the following order,
1117         and these should be indexed appropriately by each of the error messages
1118         (see the 'errmsg' variable for the format string):
1119 
1120             index 0: the argument name
1121             index 1: number of parameters
1122             index 2: configured minimum parameters
1123             index 3: configured maximum parameters
1124             index 4: conflicting/dependent argument (or invalid param)
1125             index 5: array of configured parameter options
1126 
1127         Params:
1128             errors = string to replace the default error messages with
1129 
1130         Returns:
1131             this object for method chaining
1132 
1133     ***************************************************************************/
1134 
1135     public Arguments errors ( const(istring)[] errors )
1136     {
1137         if ( errors.length is errmsg.length )
1138         {
1139             msgs = errors;
1140         }
1141         else
1142         {
1143             verify(false);
1144         }
1145 
1146         return this;
1147     }
1148 
1149 
1150     /***************************************************************************
1151 
1152         Exposes the configured help text for each of the configured arguments,
1153         via the given delegate. Note that the delegate will be called only for
1154         those arguments for which a help text has been configured.
1155 
1156         Params:
1157             dg = delegate that will be called for each argument having a help
1158                 text (the argument name and the help text itself will be sent as
1159                 parameters to the delegate)
1160 
1161         Returns:
1162             this object for method chaining
1163 
1164     ***************************************************************************/
1165 
1166     public Arguments help ( scope void delegate ( istring arg, istring help ) dg )
1167     {
1168         foreach ( arg; args )
1169         {
1170             if ( arg.text.ptr )
1171             {
1172                 dg(arg.name, arg.text);
1173             }
1174         }
1175 
1176         return this;
1177     }
1178 
1179 
1180     /***************************************************************************
1181 
1182         Displays the full help message for the application.
1183 
1184         Params:
1185             output = stream where to print the help message (Stderr by default)
1186 
1187     ***************************************************************************/
1188 
1189     public void displayHelp ( FormatOutput output = Stderr )
1190     {
1191         if ( this.short_desc.length > 0 )
1192         {
1193             output.formatln(this.short_desc, this.app_name);
1194             output.newline;
1195         }
1196 
1197         output.formatln("Usage:\t" ~ this.usage, this.app_name);
1198         output.newline;
1199 
1200         if ( this.long_desc.length > 0 )
1201         {
1202             output.formatln(this.long_desc, this.app_name);
1203             output.newline;
1204         }
1205 
1206         foreach ( arg; this.args )
1207         {
1208             this.calculateSpacing(arg);
1209         }
1210 
1211         output.formatln("Program options:");
1212 
1213         foreach ( arg; this.args )
1214         {
1215             // Skip the null argument
1216             if ( arg.name.length == 0 )
1217             {
1218                 continue;
1219             }
1220 
1221             output.formatln("{}", this.formatArgumentHelp(arg, this.tmp_buf));
1222         }
1223 
1224         output.newline;
1225     }
1226 
1227 
1228     /***************************************************************************
1229 
1230         Displays any errors that occurred.
1231 
1232         Params:
1233             output = stream where to print the errors (Stderr by default)
1234 
1235     ***************************************************************************/
1236 
1237     public void displayErrors ( FormatOutput output = Stderr )
1238     {
1239         output.format("{}", this.errors());
1240     }
1241 
1242 
1243     /***************************************************************************
1244 
1245         Convenience method to check whether an argument is set or not (i.e.
1246         whether it was found during parsing of the command-line arguments).
1247 
1248         Params:
1249             name = name of the argument
1250 
1251         Returns:
1252             true if the argument is set, false otherwise
1253 
1254     ***************************************************************************/
1255 
1256     public bool getBool ( cstring name )
1257     {
1258         auto arg = this.get(name);
1259 
1260         if ( arg )
1261         {
1262             return arg.set;
1263         }
1264         else
1265         {
1266             return false;
1267         }
1268     }
1269 
1270 
1271     /***************************************************************************
1272 
1273         Convenience method to get the integer value of the parameter assigned to
1274         an argument. This is valid only if the argument has been assigned
1275         exactly one parameter.
1276 
1277         Params:
1278             T = type of integer to return
1279             name = name of the argument
1280 
1281         Returns:
1282             integer value of the parameter assigned to the argument
1283             0 if value is missing or not a valid integer
1284 
1285     ***************************************************************************/
1286 
1287     public T getInt ( T ) ( cstring name )
1288     {
1289         auto arg = this.get(name);
1290 
1291         cstring value;
1292 
1293         if ( arg && arg.assigned.length == 1 )
1294         {
1295             value = arg.assigned[0];
1296         }
1297 
1298         T num;
1299         if (toInteger(value, num))
1300             return num;
1301         else
1302             return 0;
1303     }
1304 
1305 
1306     /***************************************************************************
1307 
1308         Convenience method to get the string parameter assigned to an argument.
1309         This is valid only if the argument has been assigned exactly one
1310         parameter.
1311 
1312         Params:
1313             name = name of the argument
1314 
1315         Returns:
1316             parameter assigned to the argument
1317 
1318     ***************************************************************************/
1319 
1320     public istring getString ( cstring name )
1321     {
1322         auto arg = this.get(name);
1323 
1324         istring value;
1325 
1326         if ( arg && arg.assigned.length == 1 )
1327         {
1328             value = arg.assigned[0];
1329         }
1330 
1331         return value;
1332     }
1333 
1334 
1335     /***************************************************************************
1336 
1337         Tests for the presence of a switch (long/short prefix) and enables the
1338         associated argument if found. Also looks for and handles explicit
1339         parameter assignment.
1340 
1341         Params:
1342             s = An individual string from the command-line arguments (includes
1343                 the long/short prefix if it is an argument string)
1344             p = the prefix string (whether this is the long prefix or the short
1345                 prefix is indicated by the 'flag' parameter)
1346             sloppy = true if any unexpected arguments found during parsing
1347                 should be accepted on-the-fly, false if unexpected arguments
1348                 should be treated as error
1349             flag = true if the prefix string given is the short prefix, false if
1350                 it is the long prefix
1351 
1352         Returns:
1353             true if the given string was an argument, false if it was a
1354             parameter
1355 
1356     ***************************************************************************/
1357 
1358     private bool argument ( istring s, istring p, bool sloppy, bool flag )
1359     {
1360         if ( s.length >= p.length && s[0 .. p.length] == p )
1361         {
1362             s = s[p.length .. $];
1363 
1364             auto i = locate(s, eq);
1365 
1366             if ( i < s.length )
1367             {
1368                 enable(s[0 .. i], sloppy, flag).append(s[i + 1 .. $], true);
1369             }
1370             else
1371             {
1372                 // trap empty arguments; attach as param to null-arg
1373                 if ( s.length )
1374                 {
1375                     enable(s, sloppy, flag);
1376                 }
1377                 else
1378                 {
1379                     this.get(null).append(p, true);
1380                 }
1381             }
1382 
1383             return true;
1384         }
1385 
1386         return false;
1387     }
1388 
1389 
1390     /***************************************************************************
1391 
1392         Indicates the existence of an argument, and handles sloppy arguments
1393         along with multiple-flags and smushed parameters. Note that sloppy
1394         arguments are configured with parameters enabled.
1395 
1396         Params:
1397             elem = an argument name found during parsing (does not contain the
1398                 long/short prefix)
1399             sloppy = true if any unexpected arguments found during parsing
1400                 should be accepted on-the-fly, false if unexpected arguments
1401                 should be treated as error
1402             flag = true if the argument name was preceded by the short prefix,
1403                 false if it was preceded by the long prefix
1404 
1405         Returns:
1406             the configured argument instance
1407 
1408     ***************************************************************************/
1409 
1410     private Argument enable ( istring elem, bool sloppy, bool flag = false )
1411     {
1412         if ( flag && elem.length > 1 )
1413         {
1414             // locate arg for first char
1415             auto arg = enable(elem[0 .. 1], sloppy);
1416 
1417             elem = elem[1 .. $];
1418 
1419             // drop further processing of this flag where in error
1420             if ( arg.error is arg.None )
1421             {
1422                 // smush remaining text or treat as additional args
1423                 if ( arg.cat )
1424                 {
1425                     arg.append(elem, true);
1426                 }
1427                 else
1428                 {
1429                     arg = enable(elem, sloppy, true);
1430                 }
1431             }
1432 
1433             return arg;
1434         }
1435 
1436         // if not in args, or in aliases, then create new arg
1437         auto a = elem in args;
1438 
1439         if ( a is null )
1440         {
1441             if ( (a = elem in aliases) is null )
1442             {
1443                 return this.get(elem).params.enable(!sloppy);
1444             }
1445         }
1446 
1447         return a.enable;
1448     }
1449 
1450 
1451     /***************************************************************************
1452 
1453         Calculates the width required to display all the aliases based on the
1454         given number of aliases (in the aliases string, each character is an
1455         individual argument alias).
1456 
1457         Params:
1458             aliases = number of argument aliases
1459 
1460         Returns:
1461             width required to display all the aliases
1462 
1463     ***************************************************************************/
1464 
1465     private size_t aliasesWidth ( size_t aliases )
1466     {
1467         auto width = aliases * 2; // *2 for a '-' before each alias
1468 
1469         if ( aliases > 1 )
1470         {
1471             width += (aliases - 1) * 2; // ', ' after each alias except the last
1472         }
1473 
1474         return width;
1475     }
1476 
1477 
1478     /***************************************************************************
1479 
1480         Calculates the maximum width required to display the given argument name
1481         and its aliases.
1482 
1483         Params:
1484             arg = the argument instance
1485 
1486     ***************************************************************************/
1487 
1488     private void calculateSpacing ( Argument arg )
1489     {
1490         this.long_name_width = max(this.long_name_width, arg.name.length);
1491 
1492         this.aliases_width = max(this.aliases_width,
1493             this.aliasesWidth(arg.aliases.length));
1494     }
1495 
1496 
1497     /***************************************************************************
1498 
1499         Formats the help text for a single argument.
1500 
1501         Params:
1502             arg = argument instance for which the help text is to be formatted
1503             buf = buffer into which to format the help text
1504 
1505         Returns:
1506             the formatted help text
1507 
1508     ***************************************************************************/
1509 
1510     private mstring formatArgumentHelp ( Argument arg, ref mstring buf )
1511     {
1512         buf.length = 0;
1513         assumeSafeAppend(buf);
1514 
1515         sformat(buf, "  ");
1516 
1517         foreach ( i, al; arg.aliases )
1518         {
1519             sformat(buf, "-{}", al);
1520 
1521             if ( i != arg.aliases.length - 1 || arg.name.length )
1522             {
1523                 sformat(buf, ", ");
1524             }
1525         }
1526 
1527         // there is no trailing ", " in this case, so add two spaces instead.
1528         if ( arg.aliases.length == 0 )
1529         {
1530             sformat(buf, "  ");
1531         }
1532 
1533         sformat(buf, "{}",
1534             this.space(this.aliases_width -
1535                        this.aliasesWidth(arg.aliases.length)));
1536 
1537         sformat(buf, "--{}{}  ",
1538             arg.name, this.space(this.long_name_width - arg.name.length));
1539 
1540         if ( arg.text.length )
1541         {
1542             sformat(buf, "{}", arg.text);
1543         }
1544 
1545         uint extras;
1546 
1547         bool params = arg.min > 0 || arg.max > 0;
1548 
1549         if ( params )              extras++;
1550         if ( arg.options.length )  extras++;
1551         if ( arg.deefalts.length ) extras++;
1552 
1553         if ( extras )
1554         {
1555             // comma separate sections if more info to come
1556             void next ( )
1557             {
1558                 extras--;
1559 
1560                 if ( extras )
1561                 {
1562                     sformat(buf, ", ");
1563                 }
1564             }
1565 
1566             sformat(buf, " (");
1567 
1568             if ( params )
1569             {
1570                 if ( arg.min == arg.max )
1571                 {
1572                     sformat(buf, "{} param{}", arg.min,
1573                         arg.min == 1 ? "" : "s");
1574                 }
1575                 else
1576                 {
1577                     sformat(buf, "{}-{} params", arg.min, arg.max);
1578                 }
1579 
1580                 next();
1581             }
1582 
1583             if ( arg.options.length )
1584             {
1585                 sformat(buf, "{}", arg.options);
1586 
1587                 next();
1588             }
1589 
1590             if ( arg.deefalts.length )
1591             {
1592                 sformat(buf, "default: {}", arg.deefalts);
1593 
1594                 next();
1595             }
1596 
1597             sformat(buf, ")");
1598         }
1599 
1600         return buf;
1601     }
1602 
1603 
1604     /***************************************************************************
1605 
1606         Creates a string with the specified number of spaces.
1607 
1608         Params:
1609             width = desired number of spaces
1610 
1611         Returns:
1612             string with desired number of spaces.
1613 
1614     ***************************************************************************/
1615 
1616     private cstring space ( size_t width )
1617     {
1618         if ( width == 0 )
1619         {
1620             return "";
1621         }
1622 
1623         this.spaces.length = width;
1624         assumeSafeAppend(this.spaces);
1625 
1626         this.spaces[] = ' ';
1627 
1628         return this.spaces;
1629     }
1630 
1631 
1632     /***************************************************************************
1633 
1634         Class that declares a specific argument instance.
1635         One of these is instantiated using one of the outer class' `get()`
1636         methods. All existing argument instances can be iterated over using the
1637         outer class' `opApply()` method.
1638 
1639     ***************************************************************************/
1640 
1641     private class Argument
1642     {
1643         /***********************************************************************
1644 
1645             Enumeration of all error identifiers
1646 
1647         ***********************************************************************/
1648 
1649         public enum
1650         {
1651             None,     // ok (no error)
1652 
1653             ParamLo,  // too few parameters were assigned to this argument
1654 
1655             ParamHi,  // too many parameters were assigned to this argument
1656 
1657             Required, // this is a required argument, but was not given
1658 
1659             Requires, // this argument depends on another argument which was not
1660                       // given
1661 
1662             Conflict, // this argument conflicts with another given argument
1663 
1664             Extra,    // unexpected argument (will not trigger an error if
1665                       // sloppy arguments are enabled)
1666 
1667             Option,   // parameter assigned is not one of the acceptable options
1668 
1669             Invalid   // invalid error
1670         }
1671 
1672 
1673         /***********************************************************************
1674 
1675             Convenience aliases
1676 
1677         ***********************************************************************/
1678 
1679         public alias void    delegate ( )               Invoker;
1680         public alias istring delegate ( istring value ) Inspector;
1681 
1682 
1683         /***********************************************************************
1684 
1685             Minimum number of parameters for this argument
1686 
1687         ***********************************************************************/
1688 
1689         public int min;
1690 
1691 
1692         /***********************************************************************
1693 
1694             Maximum number of parameters for this argument
1695 
1696         ***********************************************************************/
1697 
1698         public int max;
1699 
1700 
1701         /***********************************************************************
1702 
1703             The error code for this argument (0 => no error)
1704 
1705         ***********************************************************************/
1706 
1707         public int error;
1708 
1709 
1710         /***********************************************************************
1711 
1712             Flag to indicate whether this argument is present or not
1713 
1714         ***********************************************************************/
1715 
1716         public bool set;
1717 
1718 
1719         /***********************************************************************
1720 
1721             String in which each character is an alias for this argument
1722 
1723         ***********************************************************************/
1724 
1725         public istring aliases;
1726 
1727 
1728         /***********************************************************************
1729 
1730             The name of the argument
1731 
1732         ***********************************************************************/
1733 
1734         public istring name;
1735 
1736 
1737         /***********************************************************************
1738 
1739             The help text of the argument
1740 
1741         ***********************************************************************/
1742 
1743         public istring text;
1744 
1745 
1746         /***********************************************************************
1747 
1748             Allowed parameters for this argument (there is no restriction on the
1749             acceptable parameters if this array is empty)
1750 
1751         ***********************************************************************/
1752 
1753         public const(istring)[] options;
1754 
1755 
1756         /***********************************************************************
1757 
1758             Default parameters for this argument
1759 
1760         ***********************************************************************/
1761 
1762         public const(istring)[] deefalts;
1763 
1764 
1765         /***********************************************************************
1766 
1767             Flag to indicate whether this argument is required or not
1768 
1769         ***********************************************************************/
1770 
1771         private bool req;
1772 
1773 
1774         /***********************************************************************
1775 
1776             Flag to indicate whether this argument is smushable or not
1777 
1778         ***********************************************************************/
1779 
1780         private bool cat;
1781 
1782 
1783         /***********************************************************************
1784 
1785             Flag to indicate whether this argument can accept implicit
1786             parameters or not
1787 
1788         ***********************************************************************/
1789 
1790         private bool exp;
1791 
1792 
1793         /***********************************************************************
1794 
1795             Flag to indicate whether this argument has failed parsing or not
1796 
1797         ***********************************************************************/
1798 
1799         private bool fail;
1800 
1801 
1802         /***********************************************************************
1803 
1804             The name of the argument that conflicts with this argument
1805 
1806         ***********************************************************************/
1807 
1808         private istring bogus;
1809 
1810 
1811         /***********************************************************************
1812 
1813             Parameters assigned to this argument
1814 
1815         ***********************************************************************/
1816 
1817         private istring[] values;
1818 
1819 
1820         /***********************************************************************
1821 
1822             Invocation callback
1823 
1824         ***********************************************************************/
1825 
1826         private Invoker invoker;
1827 
1828 
1829         /***********************************************************************
1830 
1831             Inspection callback
1832 
1833         ***********************************************************************/
1834 
1835         private Inspector inspector;
1836 
1837 
1838         /***********************************************************************
1839 
1840             Argument instances that are required by this argument
1841 
1842         ***********************************************************************/
1843 
1844         private Argument[] dependees;
1845 
1846 
1847         /***********************************************************************
1848 
1849             Argument instances that this argument conflicts with
1850 
1851         ***********************************************************************/
1852 
1853         private Argument[] conflictees;
1854 
1855 
1856         /***********************************************************************
1857 
1858             Constructor.
1859 
1860             Params:
1861                 name = name of the argument
1862 
1863         ***********************************************************************/
1864 
1865         public this ( istring name )
1866         {
1867             this.name = name;
1868         }
1869 
1870 
1871         /***********************************************************************
1872 
1873             Returns:
1874                 the name of this argument
1875 
1876         ***********************************************************************/
1877 
1878         public override istring toString ( )
1879         {
1880             return name;
1881         }
1882 
1883 
1884         /***********************************************************************
1885 
1886             Returns:
1887                 parameters assigned to this argument, or the default parameters
1888                 if this argument was not present on the command-line
1889 
1890         ***********************************************************************/
1891 
1892         public const(istring)[] assigned ( )
1893         {
1894             return values.length ? values : deefalts;
1895         }
1896 
1897 
1898         /***********************************************************************
1899 
1900             Sets an alias for this argument.
1901 
1902             Params:
1903                 name = character to be used as an alias for this argument
1904 
1905             Returns:
1906                 this object for method chaining
1907 
1908         ***********************************************************************/
1909 
1910         public Argument aliased ( char name )
1911         {
1912             if ( auto arg = cast(cstring)((&name)[0..1]) in this.outer.aliases )
1913             {
1914                 verify(
1915                     false,
1916                     "Argument '" ~ this.name ~ "' cannot " ~
1917                         "be assigned alias '" ~ name ~ "' as it has " ~
1918                         "already been assigned to argument '"
1919                         ~ arg.name ~ "'."
1920                 );
1921             }
1922 
1923             this.outer.aliases[idup((&name)[0 .. 1])] = this;
1924 
1925             this.aliases ~= name;
1926 
1927             return this;
1928         }
1929 
1930 
1931         /***********************************************************************
1932 
1933             Makes this a mandatory argument.
1934 
1935             Returns:
1936                 this object for method chaining
1937 
1938         ***********************************************************************/
1939 
1940         public Argument required ( )
1941         {
1942             this.req = true;
1943 
1944             return this;
1945         }
1946 
1947 
1948         /***********************************************************************
1949 
1950             Sets this argument to depend upon another argument.
1951 
1952             Params:
1953                 arg = argument instance which is to be set as a dependency
1954 
1955             Returns:
1956                 this object for method chaining
1957 
1958         ***********************************************************************/
1959 
1960         public Argument requires ( Argument arg )
1961         {
1962             dependees ~= arg;
1963 
1964             return this;
1965         }
1966 
1967 
1968         /***********************************************************************
1969 
1970             Sets this argument to depend upon another argument.
1971 
1972             Params:
1973                 other = name of the argument which is to be set as a dependency
1974 
1975             Returns:
1976                 this object for method chaining
1977 
1978         ***********************************************************************/
1979 
1980         public Argument requires ( istring other )
1981         {
1982             return requires(this.outer.get(other));
1983         }
1984 
1985 
1986         /***********************************************************************
1987 
1988             Sets this argument to depend upon another argument.
1989 
1990             Params:
1991                 other = alias of the argument which is to be set as a dependency
1992 
1993             Returns:
1994                 this object for method chaining
1995 
1996         ***********************************************************************/
1997 
1998         public Argument requires ( char other )
1999         {
2000             return requires(cast(istring)(&other)[0 .. 1]);
2001         }
2002 
2003 
2004         /***********************************************************************
2005 
2006             Sets this argument to conflict with another argument.
2007 
2008             Params:
2009                 arg = argument instance with which this argument should conflict
2010 
2011             Returns:
2012                 this object for method chaining
2013 
2014         ***********************************************************************/
2015 
2016         public Argument conflicts ( Argument arg )
2017         {
2018             conflictees ~= arg;
2019 
2020             return this;
2021         }
2022 
2023 
2024         /***********************************************************************
2025 
2026             Sets this argument to conflict with another argument.
2027 
2028             Params:
2029                 other = name of the argument with which this argument should
2030                     conflict
2031 
2032             Returns:
2033                 this object for method chaining
2034 
2035         ***********************************************************************/
2036 
2037         public Argument conflicts ( istring other )
2038         {
2039             return conflicts(this.outer.get(other));
2040         }
2041 
2042 
2043         /***********************************************************************
2044 
2045             Sets this argument to conflict with another argument.
2046 
2047             Params:
2048                 other = alias of the argument with which this argument should
2049                     conflict
2050 
2051             Returns:
2052                 this object for method chaining
2053 
2054         ***********************************************************************/
2055 
2056         public Argument conflicts ( char other )
2057         {
2058             return conflicts(cast(istring)(&other)[0 .. 1]);
2059         }
2060 
2061 
2062         /***********************************************************************
2063 
2064             Enables parameter assignment for this argument. The minimum and
2065             maximum number of parameters are set to 0 and 42 respectively.
2066 
2067             Returns:
2068                 this object for method chaining
2069 
2070         ***********************************************************************/
2071 
2072         public Argument params ( )
2073         {
2074             return params(0, 42);
2075         }
2076 
2077 
2078         /***********************************************************************
2079 
2080             Enables parameter assignment for this argument and sets an exact
2081             count for the number of parameters required.
2082 
2083             Params:
2084                 count = the number of parameters to be set
2085 
2086             Returns:
2087                 this object for method chaining
2088 
2089         ***********************************************************************/
2090 
2091         public Argument params ( int count )
2092         {
2093             return params(count, count);
2094         }
2095 
2096 
2097         /***********************************************************************
2098 
2099             Enables parameter assignment for this argument and sets the counts
2100             of both the minimum and maximum parameters required.
2101 
2102             Params:
2103                 min = minimum number of parameters required
2104                 max = maximum number of parameters required
2105 
2106             Returns:
2107                 this object for method chaining
2108 
2109         ***********************************************************************/
2110 
2111         public Argument params ( int min, int max )
2112         {
2113             this.min = min;
2114 
2115             this.max = max;
2116 
2117             return this;
2118         }
2119 
2120 
2121         /***********************************************************************
2122 
2123             Adds a default parameter for this argument.
2124 
2125             Params:
2126                 values = default parameter to be added
2127 
2128             Returns:
2129                 this object for method chaining
2130 
2131         ***********************************************************************/
2132 
2133         public Argument defaults ( istring values )
2134         {
2135             this.deefalts ~= values;
2136 
2137             return this;
2138         }
2139 
2140 
2141         /***********************************************************************
2142 
2143             Sets an inspector for this argument. The inspector delegate gets
2144             fired when a parameter is appended to this argument.
2145             The appended parameter gets sent to the delegate as the input
2146             parameter. If the appended parameter is ok, the delegate should
2147             return null. Otherwise, it should return a text string describing
2148             the issue. A non-null return value from the delegate will trigger an
2149             error.
2150 
2151             Params:
2152                 inspector = delegate to be called when a parameter is appended
2153                     to this argument
2154 
2155             Returns:
2156                 this object for method chaining
2157 
2158         ***********************************************************************/
2159 
2160         public Argument bind ( scope Inspector inspector )
2161         {
2162             this.inspector = inspector;
2163 
2164             return this;
2165         }
2166 
2167 
2168         /***********************************************************************
2169 
2170             Sets an invoker for this argument. The invoker delegate gets
2171             fired when this argument is found during parsing.
2172 
2173             Params:
2174                 invoker = delegate to be called when this argument's declaration
2175                     is seen
2176 
2177             Returns:
2178                 this object for method chaining
2179 
2180         ***********************************************************************/
2181 
2182         public Argument bind ( scope Invoker invoker )
2183         {
2184             this.invoker = invoker;
2185 
2186             return this;
2187         }
2188 
2189 
2190         /***********************************************************************
2191 
2192             Enables/disables smushing for this argument.
2193 
2194             Smushing refers to omitting the explicit assignment symbol ('=' by
2195             default) or whitespace (when relying on implicit assignment) that
2196             separates an argument from its parameter. Note that smushing is
2197             possible only when assigning parameters to an argument using the
2198             argument's short prefix version.
2199 
2200             Params:
2201                 yes = true to enable smushing, false to disable
2202 
2203             Returns:
2204                 this object for method chaining
2205 
2206         ***********************************************************************/
2207 
2208         public Argument smush ( bool yes = true )
2209         {
2210             cat = yes;
2211 
2212             return this;
2213         }
2214 
2215 
2216         /***********************************************************************
2217 
2218             Disables implicit parameter assignment to this argument.
2219 
2220             Returns:
2221                 this object for method chaining
2222 
2223         ***********************************************************************/
2224 
2225         public Argument explicit ( )
2226         {
2227             exp = true;
2228 
2229             return this;
2230         }
2231 
2232 
2233         /***********************************************************************
2234 
2235             Changes the name of this argument (can be useful for naming the
2236             default argument).
2237 
2238             Params:
2239                 name = new name of this argument
2240 
2241             Returns:
2242                 this object for method chaining
2243 
2244         ***********************************************************************/
2245 
2246         public Argument title ( istring name )
2247         {
2248             this.name = name;
2249 
2250             return this;
2251         }
2252 
2253 
2254         /***********************************************************************
2255 
2256             Sets the help text for this argument.
2257 
2258             Params:
2259                 text = the help text to set
2260 
2261             Returns:
2262                 this object for method chaining
2263 
2264         ***********************************************************************/
2265 
2266         public Argument help ( istring text )
2267         {
2268             this.text = text;
2269 
2270             return this;
2271         }
2272 
2273 
2274         /***********************************************************************
2275 
2276             Fails the parsing immediately upon encountering this argument. This
2277             can be used for managing help text.
2278 
2279             Returns:
2280                 this object for method chaining
2281 
2282         ***********************************************************************/
2283 
2284         public Argument halt ( )
2285         {
2286             this.fail = true;
2287 
2288             return this;
2289         }
2290 
2291 
2292         /***********************************************************************
2293 
2294             Restricts parameters of this argument to be in the given set.
2295 
2296             Allocates copy of `options`
2297 
2298             Params:
2299                 options = array containing the set of acceptable parameters
2300 
2301             Returns:
2302                 this object for method chaining
2303 
2304         ***********************************************************************/
2305 
2306         public Argument restrict ( const(istring)[] options... )
2307         {
2308             this.options = options.dup;
2309 
2310             return this;
2311         }
2312 
2313 
2314         /***********************************************************************
2315 
2316             Sets the flag that indicates that this argument was found during
2317             parsing. Also calls the invoker delegate, if configured. If the
2318             argument is unexpected (i.e. was not pre-configured), then an
2319             appropriate error condition gets set.
2320 
2321             Params:
2322                 unexpected = true if this is an unexpected argument, false
2323                     otherwise
2324 
2325             Returns:
2326                 this object for method chaining
2327 
2328         ***********************************************************************/
2329 
2330         private Argument enable ( bool unexpected = false )
2331         {
2332             this.set = true;
2333 
2334             if ( max > 0 )
2335             {
2336                 this.outer.stack.push(this);
2337             }
2338 
2339             if ( invoker )
2340             {
2341                 invoker();
2342             }
2343 
2344             if ( unexpected )
2345             {
2346                 error = Extra;
2347             }
2348 
2349             return this;
2350         }
2351 
2352 
2353         /***********************************************************************
2354 
2355             Appends the given parameter to this argument. Also calls the
2356             inspector delegate, if configured.
2357 
2358             Params:
2359                 value = parameter to be appended
2360                 explicit = true if the parameter was explicitly assigned to this
2361                     argument, false otherwise (defaults to false)
2362 
2363         ***********************************************************************/
2364 
2365         private void append ( istring value, bool explicit = false )
2366         {
2367             // pop to an argument that can accept implicit parameters?
2368             if ( explicit is false )
2369             {
2370                 auto s = &(this.outer.stack);
2371 
2372                 while ( s.top.exp && s.size > 1 )
2373                 {
2374                     s.pop;
2375                 }
2376             }
2377 
2378             this.set = true; // needed for default assignments
2379 
2380             values ~= value; // append new value
2381 
2382             if ( error is None )
2383             {
2384                 if ( inspector )
2385                 {
2386                     if ( (bogus = inspector(value)).length )
2387                     {
2388                         error = Invalid;
2389                     }
2390                 }
2391 
2392                 if ( options.length )
2393                 {
2394                     error = Option;
2395 
2396                     foreach ( option; options )
2397                     {
2398                         if ( option == value )
2399                         {
2400                             error = None;
2401                         }
2402                     }
2403                 }
2404             }
2405 
2406             // pop to an argument that can accept parameters
2407             auto s = &(this.outer.stack);
2408 
2409             while ( s.top.values.length >= max && s.size > 1 )
2410             {
2411                 s.pop;
2412             }
2413         }
2414 
2415 
2416         /***********************************************************************
2417 
2418             Tests whether an error condition occurred for this argument during
2419             parsing, and if so the appropriate error code is set.
2420 
2421             Returns:
2422                 the error code for this argument (0 => no error)
2423 
2424         ***********************************************************************/
2425 
2426         private int valid ( )
2427         {
2428             if ( error is None )
2429             {
2430                 if ( req && !set )
2431                 {
2432                     error = Required;
2433                 }
2434                 else
2435                 {
2436                     if ( set )
2437                     {
2438                         // short circuit?
2439                         if ( fail )
2440                         {
2441                             return -1;
2442                         }
2443 
2444                         if ( values.length < min )
2445                         {
2446                             error = ParamLo;
2447                         }
2448                         else
2449                         {
2450                             if ( values.length > max )
2451                             {
2452                                 error = ParamHi;
2453                             }
2454                             else
2455                             {
2456                                 foreach ( arg; dependees )
2457                                 {
2458                                     if ( ! arg.set )
2459                                     {
2460                                         error = Requires;
2461                                         bogus = arg.name;
2462                                     }
2463                                 }
2464 
2465                                 foreach ( arg; conflictees )
2466                                 {
2467                                     if ( arg.set )
2468                                     {
2469                                         error = Conflict;
2470                                         bogus = arg.name;
2471                                     }
2472                                 }
2473                             }
2474                         }
2475                     }
2476                 }
2477             }
2478 
2479             return error;
2480         }
2481     }
2482 }
2483 
2484 
2485 
2486 /*******************************************************************************
2487 
2488     Unit tests
2489 
2490 *******************************************************************************/
2491 
2492 unittest
2493 {
2494     auto args = new Arguments;
2495 
2496     // basic
2497     auto x = args['x'];
2498     test(args.parse(""));
2499     x.required;
2500     test(args.parse("") is false);
2501     test(args.clear.parse("-x"));
2502     test(x.set);
2503 
2504     // alias
2505     x.aliased('X');
2506     test(args.clear.parse("-X"));
2507     test(x.set);
2508 
2509     // unexpected arg (with sloppy)
2510     test(args.clear.parse("-y") is false);
2511     test(args.clear.parse("-y") is false);
2512     test(args.clear.parse("-y", true) is false);
2513     test(args['y'].set);
2514     test(args.clear.parse("-x -y", true));
2515 
2516     // parameters
2517     x.params(0);
2518     test(args.clear.parse("-x param"));
2519     test(x.assigned.length is 0);
2520     test(args(null).assigned.length is 1);
2521     x.params(1);
2522     test(args.clear.parse("-x=param"));
2523     test(x.assigned.length is 1);
2524     test(x.assigned[0] == "param");
2525     test(args.clear.parse("-x param"));
2526     test(x.assigned.length is 1);
2527     test(x.assigned[0] == "param");
2528 
2529     // too many args
2530     x.params(1);
2531     test(args.clear.parse("-x param1 param2"));
2532     test(x.assigned.length is 1);
2533     test(x.assigned[0] == "param1");
2534     test(args(null).assigned.length is 1);
2535     test(args(null).assigned[0] == "param2");
2536 
2537     // now with default params
2538     test(args.clear.parse("param1 param2 -x=blah"));
2539     test(args[null].assigned.length is 2);
2540     test(args(null).assigned.length is 2);
2541     test(x.assigned.length is 1);
2542     x.params(0);
2543     test(!args.clear.parse("-x=blah"));
2544 
2545     // args as parameter
2546     test(args.clear.parse("- -x"));
2547     test(args[null].assigned.length is 1);
2548     test(args[null].assigned[0] == "-");
2549 
2550     // multiple flags, with alias and sloppy
2551     test(args.clear.parse("-xy"));
2552     test(args.clear.parse("-xyX"));
2553     test(x.set);
2554     test(args['y'].set);
2555     test(args.clear.parse("-xyz") is false);
2556     test(args.clear.parse("-xyz", true));
2557     auto z = args['z'];
2558     test(z.set);
2559 
2560     // multiple flags with trailing arg
2561     test(args.clear.parse("-xyz=10"));
2562     test(z.assigned.length is 1);
2563 
2564     // again, but without sloppy param declaration
2565     z.params(0);
2566     test(!args.clear.parse("-xyz=10"));
2567     test(args.clear.parse("-xzy=10"));
2568     test(args('y').assigned.length is 1);
2569     test(args('x').assigned.length is 0);
2570     test(args('z').assigned.length is 0);
2571 
2572     // x requires y
2573     x.requires('y');
2574     test(args.clear.parse("-xy"));
2575     test(args.clear.parse("-xz") is false);
2576     test!("==")(args.errors(), "argument 'x' requires 'y'\n");
2577 
2578     // defaults
2579     z.defaults("foo");
2580     test(args.clear.parse("-xy"));
2581     test(z.assigned.length is 1);
2582 
2583     // long names, with params
2584     test(args.clear.parse("-xy --foobar") is false);
2585     test(args.clear.parse("-xy --foobar", true));
2586     test(args["y"].set && x.set);
2587     test(args["foobar"].set);
2588     test(args.clear.parse("-xy --foobar=10"));
2589     test(args["foobar"].assigned.length is 1);
2590     test(args["foobar"].assigned[0] == "10");
2591 
2592     // smush argument z, but not others
2593     z.params;
2594     test(args.clear.parse("-xy -zsmush") is false);
2595     test(x.set);
2596     z.smush;
2597     test(args.clear.parse("-xy -zsmush"));
2598     test(z.assigned.length is 1);
2599     test(z.assigned[0] == "smush");
2600     test(x.assigned.length is 0);
2601     z.params(0);
2602 
2603     // conflict x with z
2604     x.conflicts(z);
2605     test(args.clear.parse("-xyz") is false);
2606 
2607     // word mode, with prefix elimination
2608     args = new Arguments(null, null, null, null, null, null);
2609     test(args.clear.parse("foo bar wumpus") is false);
2610     test(args.clear.parse("foo bar wumpus wombat", true));
2611     test(args("foo").set);
2612     test(args("bar").set);
2613     test(args("wumpus").set);
2614     test(args("wombat").set);
2615 
2616     // use '/' instead of '-'
2617     args = new Arguments(null, null, null, null, "/", "/");
2618     test(args.clear.parse("/foo /bar /wumpus") is false);
2619     test(args.clear.parse("/foo /bar /wumpus /wombat", true));
2620     test(args("foo").set);
2621     test(args("bar").set);
2622     test(args("wumpus").set);
2623     test(args("wombat").set);
2624 
2625     // use '/' for short and '-' for long
2626     args = new Arguments(null, null, null, null, "/", "-");
2627     test(args.clear.parse("-foo -bar -wumpus -wombat /abc", true));
2628     test(args("foo").set);
2629     test(args("bar").set);
2630     test(args("wumpus").set);
2631     test(args("wombat").set);
2632     test(args("a").set);
2633     test(args("b").set);
2634     test(args("c").set);
2635 
2636     // "--" makes all subsequent be implicit parameters
2637     args = new Arguments;
2638     args('f').params(0);
2639     test(args.parse("-f -- -bar -wumpus -wombat --abc"));
2640     test(args('f').assigned.length is 0);
2641     test(args(null).assigned.length is 4);
2642 
2643     // Confirm arguments are stored in a sorted manner
2644     args = new Arguments;
2645     test(args.clear.parse("--beta --alpha --delta --echo --charlie", true));
2646     size_t index;
2647     foreach (arg; args)
2648     {
2649         switch ( index++ )
2650         {
2651             case 0: continue;
2652             case 1: test("alpha"   == arg.name); continue;
2653             case 2: test("beta"    == arg.name); continue;
2654             case 3: test("charlie" == arg.name); continue;
2655             case 4: test("delta"   == arg.name); continue;
2656             case 5: test("echo"    == arg.name); continue;
2657             default: test(0);
2658         }
2659     }
2660 
2661     // Test that getInt() works as expected
2662     args = new Arguments;
2663     args("num").params(1);
2664     test(args.parse("--num 100"));
2665     test(args.getInt!(uint)("num") == 100);
2666 
2667     args = new Arguments;
2668     args("num").params(1);
2669     test(args.parse("--num 18446744073709551615"));
2670     test(args.getInt!(ulong)("num") == ulong.max);
2671 
2672     args = new Arguments;
2673     args("num").params(1);
2674     test(args.getInt!(ulong)("num") == 0);
2675 }
2676 
2677 // Test for D2 'static immutable'
2678 unittest
2679 {
2680     static immutable istring name_ = "encode";
2681     static immutable istring conflicts_ = "decode";
2682     static immutable istring[] restrict_ = [ "json", "yaml" ];
2683     static immutable istring requires_ = "input";
2684     static immutable istring help_ = "Convert from native format to JSON/Yaml";
2685 
2686     auto args = new Arguments;
2687     args(name_)
2688         .params(1)
2689         .conflicts(conflicts_)
2690         .restrict(restrict_)
2691         .requires(requires_)
2692         .help(help_);
2693 
2694 }