1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2009 Kris. All rights reserved.
4 
5         license:        BSD style: $(LICENSE)
6         
7         version:        Oct 2009: Initial release
8         
9         author:         Kris
10     
11 *******************************************************************************/
12 
13 module tango.text.Arguments;
14 
15 private import tango.text.Util;
16 private import tango.util.container.more.Stack;
17 
18 version=dashdash;       // -- everything assigned to the null argument
19 
20 /*******************************************************************************
21 
22         Command-line argument parser. Simple usage is:
23         ---
24         auto args = new Arguments;
25         args.parse ("-a -b", true);
26         auto a = args("a");
27         auto b = args("b");
28         if (a.set && b.set)
29             ...
30         ---
31 
32         Argument parameters are assigned to the last known target, such
33         that multiple parameters accumulate:
34         ---
35         args.parse ("-a=1 -a=2 foo", true);
36         assert (args('a').assigned().length is 3);
37         ---
38 
39         That example results in argument 'a' assigned three parameters.
40         Two parameters are explicitly assigned using '=', while a third
41         is implicitly assigned(). Implicit parameters are often useful for
42         collecting filenames or other parameters without specifying the
43         associated argument:
44         ---
45         args.parse ("thisfile.txt thatfile.doc -v", true);
46         assert (args(null).assigned().length is 2);
47         ---
48         The 'null' argument is always defined and acts as an accumulator
49         for parameters left uncaptured by other arguments. In the above
50         instance it was assigned both parameters. 
51         
52         Examples thus far have used 'sloppy' argument declaration, via
53         the second argument of parse() being set true. This allows the
54         parser to create argument declaration on-the-fly, which can be
55         handy for trivial usage. However, most features require the a-
56         priori declaration of arguments:
57         ---
58         args = new Arguments;
59         args('x').required;
60         if (! args.parse("-x"))
61               // x not supplied!
62         ---
63 
64         Sloppy arguments are disabled in that example, and a required
65         argument 'x' is declared. The parse() method will fail if the
66         pre-conditions are not fully met. Additional qualifiers include
67         specifying how many parameters are allowed for each individual
68         argument, default parameters, whether an argument requires the 
69         presence or exclusion of another, etc. Qualifiers are typically 
70         chained together and the following example shows argument "foo"
71         being made required, with one parameter, aliased to 'f', and
72         dependent upon the presence of another argument "bar":
73         ---
74         args("foo").required.params(1).aliased('f').requires("bar");
75         args("help").aliased('?').aliased('h');
76         ---
77 
78         Parameters can be constrained to a set of matching text values,
79         and the parser will fail on mismatched input:
80         ---
81         args("greeting").restrict("hello", "yo", "gday");
82         args("enabled").restrict("true", "false", "t", "f", "y", "n");  
83         ---
84 
85         A set of declared arguments may be configured in this manner
86         and the parser will return true only where all conditions are
87         met. Where a error condition occurs you may traverse the set
88         of arguments to find out which argument has what error. This
89         can be handled like so, where arg.error holds a defined code:
90         ---
91         if (! args.parse (...))
92               foreach (arg; args)
93                        if (arg.error)
94                            ...
95         ---
96        
97         Error codes are as follows:
98         ---
99         None:           ok (zero)
100         ParamLo:        too few params for an argument
101         ParamHi:        too many params for an argument
102         Required:       missing argument is required 
103         Requires:       depends on a missing argument
104         Conflict:       conflicting argument is present
105         Extra:          unexpected argument (see sloppy)
106         Option:         parameter does not match options
107         ---
108         
109         A simpler way to handle errors is to invoke an internal format
110         routine, which constructs error messages on your behalf:
111         ---
112         if (! args.parse (...))
113               stderr (args.errors(&stderr.layout.sprint));
114         ---
115 
116         Note that messages are constructed via a layout handler and
117         the messages themselves may be customized (for i18n purposes).
118         See the two errors() methods for more information on this.
119 
120         The parser make a distinction between a short and long prefix, 
121         in that a long prefix argument is always distinct while short
122         prefix arguments may be combined as a shortcut:
123         ---
124         args.parse ("--foo --bar -abc", true);
125         assert (args("foo").set);
126         assert (args("bar").set);
127         assert (args("a").set);
128         assert (args("b").set);
129         assert (args("c").set);
130         ---
131 
132         In addition, short-prefix arguments may be "smushed" with an
133         associated parameter when configured to do so:
134         ---
135         args('o').params(1).smush;
136         if (args.parse ("-ofile"))
137             assert (args('o').assigned()[0] == "file");
138         ---
139 
140         There are two callback varieties supports, where one is invoked
141         when an associated argument is parsed and the other is invoked
142         as parameters are assigned(). See the bind() methods for delegate
143         signature details.
144 
145         You may change the argument prefix to be something other than 
146         "-" and "--" via the constructor. You might, for example, need 
147         to specify a "/" indicator instead, and use ':' for explicitly
148         assigning parameters:
149         ---
150         auto args = new Args ("/", "-", ':');
151         args.parse ("-foo:param -bar /abc");
152         assert (args("foo").set);
153         assert (args("bar").set);
154         assert (args("a").set);
155         assert (args("b").set);
156         assert (args("c").set);
157         assert (args("foo").assigned().length is 1);
158         ---
159 
160         Returning to an earlier example we can declare some specifics:
161         ---
162         args('v').params(0);
163         assert (args.parse (`-v thisfile.txt thatfile.doc`));
164         assert (args(null).assigned().length is 2);
165         ---
166 
167         Note that the -v flag is now in front of the implicit parameters
168         but ignores them because it is declared to consume none. That is,
169         implicit parameters are assigned to arguments from right to left,
170         according to how many parameters said arguments may consume. Each
171         sloppy argument consumes parameters by default, so those implicit
172         parameters would have been assigned to -v without the declaration 
173         shown. On the other hand, an explicit assignment (via '=') always 
174         associates the parameter with that argument even when an overflow
175         would occur (though will cause an error to be raised).
176 
177         Certain parameters are used for capturing comments or other plain
178         text from the user, including whitespace and other special chars.
179         Such parameter values should be quoted on the commandline, and be
180         assigned explicitly rather than implicitly:
181         ---
182         args.parse (`--comment="-- a comment --"`);
183         ---
184 
185         Without the explicit assignment, the text content might otherwise 
186         be considered the start of another argument (due to how argv/argc
187         values are stripped of original quotes).
188 
189         Lastly, all subsequent text is treated as paramter-values after a
190         "--" token is encountered. This notion is applied by unix systems 
191         to terminate argument processing in a similar manner. Such values
192         are considered to be implicit, and are assigned to preceding args
193         in the usual right to left fashion (or to the null argument):
194         ---
195         args.parse (`-- -thisfile --thatfile`);
196         assert (args(null).assigned().length is 2);
197         ---
198         
199 *******************************************************************************/
200 
201 class Arguments
202 {
203         public alias get                opCall;         // args("name")
204         public alias get                opIndex;        // args["name"]
205 
206         private Stack!(Argument)        stack;          // args with params
207         private Argument[const(char)[]] args;           // the set of args
208         private Argument[const(char)[]] aliases;        // set of aliases
209         private char                    eq;             // '=' or ':'
210         private const(char)[]           sp,             // short prefix
211                                         lp;             // long prefix
212         private const(char[])[]         msgs = errmsg;  // error messages
213         private __gshared const const(char[])[] errmsg = // default errors
214                 [
215                 "argument '{0}' expects {2} parameter(s) but has {1}\n", 
216                 "argument '{0}' expects {3} parameter(s) but has {1}\n", 
217                 "argument '{0}' is missing\n", 
218                 "argument '{0}' requires '{4}'\n", 
219                 "argument '{0}' conflicts with '{4}'\n", 
220                 "unexpected argument '{0}'\n", 
221                 "argument '{0}' expects one of {5}\n", 
222                 "invalid parameter for argument '{0}': {4}\n", 
223                 ];
224 
225         /***********************************************************************
226               
227               Construct with the specific short & long prefixes, and the 
228               given assignment character (typically ':' on Windows but we
229               set the defaults to look like unix instead)
230 
231         ***********************************************************************/
232         
233         this (const(char)[] sp="-", const(char)[] lp="--", char eq='=')
234         {
235                 this.sp = sp;
236                 this.lp = lp;
237                 this.eq = eq;
238                 get(null).params();       // set null argument to consume params
239         }
240 
241         /***********************************************************************
242               
243                 Parse string[] into a set of Argument instances. The 'sloppy'
244                 option allows for unexpected arguments without error.
245                 
246                 Returns false where an error condition occurred, whereupon the 
247                 arguments should be traversed to discover said condition(s):
248                 ---
249                 auto args = new Arguments;
250                 if (! args.parse (...))
251                       stderr (args.errors(&stderr.layout.sprint));
252                 ---
253 
254         ***********************************************************************/
255         
256         final bool parse (const(char)[] input, bool sloppy=false)
257         {
258                 const(char[])[] tmp;
259                 foreach (s; quotes(input, " "))
260                          tmp ~= s;
261                 return parse (tmp, sloppy);
262         }
263 
264         /***********************************************************************
265               
266                 Parse a string into a set of Argument instances. The 'sloppy'
267                 option allows for unexpected arguments without error.
268                 
269                 Returns false where an error condition occurred, whereupon the 
270                 arguments should be traversed to discover said condition(s):
271                 ---
272                 auto args = new Arguments;
273                 if (! args.parse (...))
274                       Stderr (args.errors(&Stderr.layout.sprint));
275                 ---
276 
277         ***********************************************************************/
278         
279         final bool parse (const(char[])[] input, bool sloppy=false)
280         {
281                 bool    done;
282                 int     error;
283 
284                 debug(Arguments) stdout.formatln ("\ncmdline: '{}'", input);
285                 stack.push (get(null));
286                 foreach (s; input)
287                         {
288                         debug(Arguments) stdout.formatln ("'{}'", s);
289                         if (done is false)
290                         {
291                             if (s == "--")
292                                {done=true; version(dashdash){stack.clear().push(get(null));} continue;}
293                             else
294                                if (argument (s, lp, sloppy, false) ||
295                                    argument (s, sp, sloppy, true))
296                                    continue;
297                         }
298                         stack.top.append (s);
299                         }  
300                 foreach (arg; args)
301                          error |= arg.valid();
302                 return error is 0;
303         }
304 
305         /***********************************************************************
306               
307                 Clear parameter assignments, flags and errors. Note this 
308                 does not remove any Arguments
309 
310         ***********************************************************************/
311         
312         final Arguments clear ()
313         {
314                 stack.clear();
315                 foreach (arg; args)
316                         {
317                         arg.set = false;
318                         arg.values = null;
319                         arg.error = arg.None;
320                         }
321                 return this;
322         }
323 
324         /***********************************************************************
325               
326                 Obtain an argument reference, creating an new instance where
327                 necessary. Use array indexing or opCall syntax if you prefer
328 
329         ***********************************************************************/
330         
331         final Argument get (char name)
332         {
333                 return get ((&name)[0..1]);
334         }
335 
336         /***********************************************************************
337               
338                 Obtain an argument reference, creating an new instance where
339                 necessary. Use array indexing or opCall syntax if you prefer.
340 
341                 Pass null to access the 'default' argument (where unassigned
342                 implicit parameters are gathered)
343                 
344         ***********************************************************************/
345         
346         final Argument get (const(char)[] name)
347         {
348                 auto a = name in args;
349                 if (a is null)
350                    {name=name.dup; return args[name] = new Argument(name);}
351                 return *a;
352         }
353 
354         /***********************************************************************
355 
356                 Traverse the set of arguments
357 
358         ***********************************************************************/
359 
360         final int opApply (scope int delegate(ref Argument) dg)
361         {
362                 int result;
363                 foreach (arg; args)  
364                          if ((result=dg(arg)) != 0)
365                               break;
366                 return result;
367         }
368 
369         /***********************************************************************
370 
371                 Construct a string of error messages, using the given
372                 delegate to format the output. You would typically pass
373                 the system formatter here, like so:
374                 ---
375                 auto msgs = args.errors (&stderr.layout.sprint);
376                 ---
377 
378                 The messages are replacable with custom (i18n) versions
379                 instead, using the errors(char[][]) method 
380 
381         ***********************************************************************/
382 
383         final char[] errors (char[] delegate(char[] buf, const(char)[] fmt, ...) dg)
384         {
385                 char[256] tmp;
386                 char[] result;
387                 foreach (arg; args)
388                 {
389                          if (arg.error)
390                              result ~= dg (tmp, msgs[arg.error-1], arg.name, 
391                                            arg.values.length, arg.min, arg.max, 
392                                            arg.bogus, arg.options);
393                 }
394                 return result;
395         }
396 
397         /***********************************************************************
398                 
399                 Use this method to replace the default error messages. Note
400                 that arguments are passed to the formatter in the following
401                 order, and these should be indexed appropriately by each of
402                 the error messages (see examples in errmsg above):
403                 ---
404                 index 0: the argument name
405                 index 1: number of parameters
406                 index 2: configured minimum parameters
407                 index 3: configured maximum parameters
408                 index 4: conflicting/dependent argument (or invalid param)
409                 index 5: array of configured parameter options
410                 ---
411 
412         ***********************************************************************/
413 
414         final Arguments errors (const(char[])[] errors)
415         {
416                 if (errors.length is errmsg.length)
417                     msgs = errors;
418                 else
419                    assert (false);
420                 return this;
421         }
422 
423         /***********************************************************************
424                 
425                 Expose the configured set of help text, via the given 
426                 delegate
427 
428         ***********************************************************************/
429 
430         final Arguments help (scope void delegate(const(char)[] arg, const(char)[] help) dg)
431         {
432                 foreach (arg; args)
433                          if (arg.text.ptr)
434                              dg (arg.name, arg.text);
435                 return this;
436         }
437 
438         /***********************************************************************
439               
440                 Test for the presence of a switch (long/short prefix) 
441                 and enable the associated arg where found. Also look 
442                 for and handle explicit parameter assignment
443                 
444         ***********************************************************************/
445         
446         private bool argument (const(char)[] s, const(char)[] p, bool sloppy, bool flag)
447         {
448                 if (s.length >= p.length && s[0..p.length] == p)
449                    {
450                    s = s [p.length..$];
451                    auto i = locate (s, eq);
452                    if (i < s.length)
453                        enable (s[0..i], sloppy, flag).append (s[i+1..$], true);
454                    else
455                       // trap empty arguments; attach as param to null-arg
456                       if (s.length)
457                           enable (s, sloppy, flag);
458                       else
459                          get(null).append (p, true);
460                    return true;
461                    }
462                 return false;
463         }
464 
465         /***********************************************************************
466               
467                 Indicate the existance of an argument, and handle sloppy
468                 options along with multiple-flags and smushed parameters.
469                 Note that sloppy arguments are configured with parameters
470                 enabled.
471 
472         ***********************************************************************/
473         
474         private Argument enable (const(char)[] elem, bool sloppy, bool flag=false)
475         {
476                 if (flag && elem.length > 1)
477                    {
478                    // locate arg for first char
479                    auto arg = enable (elem[0..1], sloppy);
480                    elem = elem[1..$];
481 
482                    // drop further processing of this flag where in error
483                    if (arg.error is arg.None)
484                    {
485                        // smush remaining text or treat as additional args
486                        if (arg.cat)
487                            arg.append (elem, true);
488                        else
489                           arg = enable (elem, sloppy, true);
490                    }
491                    return arg;
492                    }
493 
494                 // if not in args, or in aliases, then create new arg
495                 auto a = elem in args;
496                 if (a is null)
497                     if ((a = elem in aliases) is null)
498                          return get(elem).params().enable(!sloppy);
499                 return a.enable();
500         }
501 
502         /***********************************************************************
503               
504                 A specific argument instance. You get one of these from 
505                 Arguments.get() and visit them via Arguments.opApply()
506 
507         ***********************************************************************/
508         
509         class Argument
510         {       
511                 /***************************************************************
512                 
513                         Error identifiers:
514                         ---
515                         None:           ok
516                         ParamLo:        too few params for an argument
517                         ParamHi:        too many params for an argument
518                         Required:       missing argument is required 
519                         Requires:       depends on a missing argument
520                         Conflict:       conflicting argument is present
521                         Extra:          unexpected argument (see sloppy)
522                         Option:         parameter does not match options
523                         ---
524 
525                 ***************************************************************/
526         
527                 enum {None, ParamLo, ParamHi, Required, Requires, Conflict, Extra, Option, Invalid};
528 
529                 alias void   delegate() Invoker;
530                 alias const(char)[] delegate(const(char)[] value) Inspector;
531 
532                 public int              min,            /// minimum params
533                                         max,            /// maximum params
534                                         error;          /// error condition
535                 public  bool            set;            /// arg is present
536                 public  char[]          aliases;        /// Array of aliases
537                 private bool            req,            // arg is required
538                                         cat,            // arg is smushable
539                                         exp,            // implicit params
540                                         fail;           // fail the parse
541                 public  const(char)[]   name,           // arg name
542                                         text;           // help text
543                 private const(char)[]   bogus;          // name of conflict
544                 private const(char)[][] values,         // assigned values
545                                         options,        // validation options
546                                         deefalts;       // configured defaults
547                 private Invoker         invoker;        // invocation callback
548                 private Inspector       inspector;      // inspection callback
549                 private Argument[]      dependees,      // who we require
550                                         conflictees;    // who we conflict with
551                 
552                 /***************************************************************
553               
554                         Create with the given name
555 
556                 ***************************************************************/
557         
558                 this (const(char)[] name)
559                 {
560                         this.name = name;
561                 }
562 
563                 /***************************************************************
564               
565                         Return the name of this argument
566 
567                 ***************************************************************/
568         
569                 override immutable(char)[] toString()
570                 {
571                         return name.idup;
572                 }
573 
574                 /***************************************************************
575                 
576                         return the assigned parameters, or the defaults if
577                         no parameters were assigned
578 
579                 ***************************************************************/
580         
581                 final const(char[])[] assigned ()
582                 {
583                         return values.length ? values : deefalts;
584                 }
585 
586                 /***************************************************************
587               
588                         Alias this argument with the given name. If you need 
589                         long-names to be aliased, create the long-name first
590                         and alias it to a short one
591 
592                 ***************************************************************/
593         
594                 final Argument aliased (char name)
595                 {
596                         this.outer.aliases[(&name)[0..1].idup] = this;
597                         this.aliases ~= name;
598                         return this;
599                 }
600 
601                 /***************************************************************
602               
603                         Make this argument a requirement
604 
605                 ***************************************************************/
606         
607                 @property final Argument required ()
608                 {
609                         this.req = true;
610                         return this;
611                 }
612 
613                 /***************************************************************
614               
615                         Set this argument to depend upon another
616 
617                 ***************************************************************/
618         
619                 final Argument requires (Argument arg)
620                 {
621                         dependees ~= arg;
622                         return this;
623                 }
624 
625                 /***************************************************************
626               
627                         Set this argument to depend upon another
628 
629                 ***************************************************************/
630         
631                 final Argument requires (const(char)[] other)
632                 {
633                         return requires (this.outer.get(other));
634                 }
635 
636                 /***************************************************************
637               
638                         Set this argument to depend upon another
639 
640                 ***************************************************************/
641         
642                 final Argument requires (char other)
643                 {
644                         return requires ((&other)[0..1]);
645                 }
646 
647                 /***************************************************************
648               
649                         Set this argument to conflict with another
650 
651                 ***************************************************************/
652         
653                 final Argument conflicts (Argument arg)
654                 {
655                         conflictees ~= arg;
656                         return this;
657                 }
658 
659                 /***************************************************************
660               
661                         Set this argument to conflict with another
662 
663                 ***************************************************************/
664         
665                 final Argument conflicts (const(char)[] other)
666                 {
667                         return conflicts (this.outer.get(other));
668                 }
669 
670                 /***************************************************************
671               
672                         Set this argument to conflict with another
673 
674                 ***************************************************************/
675         
676                 final Argument conflicts (char other)
677                 {
678                         return conflicts ((&other)[0..1]);
679                 }
680 
681                 /***************************************************************
682               
683                         Enable parameter assignment: 0 to 42 by default
684 
685                 ***************************************************************/
686         
687                 final Argument params ()
688                 {
689                         return params (0, 42);
690                 }
691 
692                 /***************************************************************
693               
694                         Set an exact number of parameters required
695 
696                 ***************************************************************/
697         
698                 final Argument params (int count)
699                 {
700                         return params (count, count);
701                 }
702 
703                 /***************************************************************
704               
705                         Set both the minimum and maximum parameter counts
706 
707                 ***************************************************************/
708         
709                 final Argument params (int min, int max)
710                 {
711                         this.min = min;
712                         this.max = max;
713                         return this;
714                 }
715 
716                 /***************************************************************
717                         
718                         Add another default parameter for this argument
719 
720                 ***************************************************************/
721         
722                 final Argument defaults (const(char)[] values)
723                 {
724                         this.deefalts ~= values;
725                         return this;
726                 }
727 
728                 /***************************************************************
729               
730                         Set an inspector for this argument, fired when a
731                         parameter is appended to an argument. Return null
732                         from the delegate when the value is ok, or a text
733                         string describing the issue to trigger an error
734 
735                 ***************************************************************/
736         
737                 final Argument bind (Inspector inspector)
738                 {
739                         this.inspector = inspector;
740                         return this;
741                 }
742 
743                 /***************************************************************
744               
745                         Set an invoker for this argument, fired when an
746                         argument declaration is seen
747 
748                 ***************************************************************/
749         
750                 final Argument bind (Invoker invoker)
751                 {
752                         this.invoker = invoker;
753                         return this;
754                 }
755 
756                 /***************************************************************
757               
758                         Enable smushing for this argument, where "-ofile" 
759                         would result in "file" being assigned to argument 
760                         'o'
761 
762                 ***************************************************************/
763         
764                 final Argument smush (bool yes=true)
765                 {
766                         cat = yes;
767                         return this;
768                 }
769 
770                 /***************************************************************
771               
772                         Disable implicit arguments
773 
774                 ***************************************************************/
775         
776                 @property final Argument explicit ()
777                 {
778                         exp = true;
779                         return this;
780                 }
781 
782                 /***************************************************************
783               
784                         Alter the title of this argument, which can be 
785                         useful for naming the default argument
786 
787                 ***************************************************************/
788         
789                 final Argument title (const(char)[] name)
790                 {
791                         this.name = name;
792                         return this;
793                 }
794 
795                 /***************************************************************
796               
797                         Set the help text for this argument
798 
799                 ***************************************************************/
800         
801                 final Argument help (const(char)[] text)
802                 {
803                         this.text = text;
804                         return this;
805                 }
806 
807                 /***************************************************************
808               
809                         Fail the parse when this arg is encountered. You
810                         might use this for managing help text
811 
812                 ***************************************************************/
813         
814                 final Argument halt ()
815                 {
816                         this.fail = true;
817                         return this;
818                 }
819 
820                 /***************************************************************
821               
822                         Restrict values to one of the given set
823 
824                 ***************************************************************/
825         
826                 final Argument restrict (const(char[])[] options ...)
827                 {
828                         this.options = cast(const(char)[][])options;
829                         return this;
830                 }
831 
832                 /***************************************************************
833               
834                         This arg is present, but set an error condition
835                         (Extra) when unexpected and sloppy is not enabled.
836                         Fires any configured invoker callback.
837 
838                 ***************************************************************/
839         
840                 private Argument enable (bool unexpected=false)
841                 {
842                         this.set = true;
843                         if (max > 0)
844                             this.outer.stack.push(this);
845 
846                         if (invoker)
847                             invoker();
848                         if (unexpected)
849                             error = Extra;
850                         return this;
851                 }
852 
853                 /***************************************************************
854               
855                         Append a parameter value, invoking an inspector as
856                         necessary
857 
858                 ***************************************************************/
859         
860                 private void append (const(char)[] value, bool explicit=false)
861                 {
862                         Argument arg = this;
863                         // pop to an argument that can accept implicit parameters?
864                         if (explicit is false)
865                             for (auto s=&this.outer.stack; arg.exp && s.size>1; arg=s.top)
866                                  s.pop();
867 
868                         arg.actually_append(value);
869                         // pop to an argument that can accept parameters
870                         for (auto s=&this.outer.stack; arg.values.length >= max && s.size>1; arg=s.top)
871                              s.pop();
872                 }
873 
874                 private void actually_append (const(char)[] value)
875                 {
876                         set = true;             // needed for default assignments
877                         values ~= value;        // append new value
878 
879                         if (error is None)
880                            {
881                            if (inspector)
882                                if ((bogus = inspector(value)).length)
883                                     error = Invalid;
884 
885                            if (options.length)
886                               {
887                               error = Option;
888                               foreach (option; options)
889                                        if (option == value)
890                                            error = None;
891                               }
892                            }
893                 }
894 
895                 /***************************************************************
896                 
897                         Test and set the error flag appropriately 
898 
899                 ***************************************************************/
900         
901                 private int valid ()
902                 {
903                         if (error is None)
904                         {
905                             if (req && !set)
906                                 error = Required;
907                             else if (set)
908                             {
909                                   // short circuit?
910                                   if (fail)
911                                       return -1;
912 
913                                   if (values.length < min)
914                                       error = ParamLo;
915                                   else
916                                      if (values.length > max)
917                                          error = ParamHi;
918                                      else
919                                         {
920                                         foreach (arg; dependees)
921                                                  if (! arg.set)
922                                                        error = Requires, bogus=arg.name;
923 
924                                         foreach (arg; conflictees)
925                                                  if (arg.set)
926                                                      error = Conflict, bogus=arg.name;
927                                         }
928                             }
929                         }
930                         debug(Arguments) stdout.formatln ("{}: error={}, set={}, min={}, max={}, " ~
931                                                "req={}, values={}, defaults={}, requires={}", 
932                                                name, error, set, min, max, req, values, 
933                                                deefalts, dependees);
934                         return error;
935                 }
936         }
937 }
938 
939 
940 /*******************************************************************************
941       
942 *******************************************************************************/
943 
944 debug(UnitTest)
945 {
946         unittest
947         {
948         auto args = new Arguments;
949 
950         // basic 
951         auto x = args['x'];
952         assert (args.parse (""));
953         x.required;
954         assert (args.parse ("") is false);
955         assert (args.clear().parse ("-x"));
956         assert (x.set);
957 
958         // alias
959         x.aliased('X');
960         assert (args.clear().parse ("-X"));
961         assert (x.set);
962 
963         // unexpected arg (with sloppy)
964         assert (args.clear().parse ("-y") is false);
965         assert (args.clear().parse ("-y") is false);
966         assert (args.clear().parse ("-y", true) is false);
967         assert (args['y'].set);
968         assert (args.clear().parse ("-x -y", true));
969 
970         // parameters
971         x.params(0);
972         assert (args.clear().parse ("-x param"));
973         assert (x.assigned().length is 0);
974         assert (args(null).assigned().length is 1);
975         x.params(1);
976         assert (args.clear().parse ("-x=param"));
977         assert (x.assigned().length is 1);
978         assert (x.assigned()[0] == "param");
979         assert (args.clear().parse ("-x param"));
980         assert (x.assigned().length is 1);
981         assert (x.assigned()[0] == "param");
982 
983         // too many args
984         x.params(1);
985         assert (args.clear().parse ("-x param1 param2"));
986         assert (x.assigned().length is 1);
987         assert (x.assigned()[0] == "param1");
988         assert (args(null).assigned().length is 1);
989         assert (args(null).assigned()[0] == "param2");
990         
991         // now with default params
992         assert (args.clear().parse ("param1 param2 -x=blah"));
993         assert (args[null].assigned().length is 2);
994         assert (args(null).assigned().length is 2);
995         assert (x.assigned().length is 1);
996         x.params(0);
997         assert (!args.clear().parse ("-x=blah"));
998 
999         // args as parameter
1000         assert (args.clear().parse ("- -x"));
1001         assert (args[null].assigned().length is 1);
1002         assert (args[null].assigned()[0] == "-");
1003 
1004         // multiple flags, with alias and sloppy
1005         assert (args.clear().parse ("-xy"));
1006         assert (args.clear().parse ("-xyX"));
1007         assert (x.set);
1008         assert (args['y'].set);
1009         assert (args.clear().parse ("-xyz") is false);
1010         assert (args.clear().parse ("-xyz", true));
1011         auto z = args['z'];
1012         assert (z.set);
1013 
1014         // multiple flags with trailing arg
1015         assert (args.clear().parse ("-xyz=10"));
1016         assert (z.assigned().length is 1);
1017 
1018         // again, but without sloppy param declaration
1019         z.params(0);
1020         assert (!args.clear().parse ("-xyz=10"));
1021         assert (args.clear().parse ("-xzy=10"));
1022         assert (args('y').assigned().length is 1);
1023         assert (args('x').assigned().length is 0);
1024         assert (args('z').assigned().length is 0);
1025 
1026         // x requires y
1027         x.requires('y');
1028         assert (args.clear().parse ("-xy"));
1029         assert (args.clear().parse ("-xz") is false);
1030 
1031         // defaults
1032         z.defaults("foo");
1033         assert (args.clear().parse ("-xy"));
1034         assert (z.assigned().length is 1);
1035 
1036         // long names, with params
1037         assert (args.clear().parse ("-xy --foobar") is false);
1038         assert (args.clear().parse ("-xy --foobar", true));
1039         assert (args["y"].set && x.set);
1040         assert (args["foobar"].set);
1041         assert (args.clear().parse ("-xy --foobar=10"));
1042         assert (args["foobar"].assigned().length is 1);
1043         assert (args["foobar"].assigned()[0] == "10");
1044 
1045         // smush argument z, but not others
1046         z.params();
1047         assert (args.clear().parse ("-xy -zsmush") is false);
1048         assert (x.set);
1049         z.smush();
1050         assert (args.clear().parse ("-xy -zsmush"));
1051         assert (z.assigned().length is 1);
1052         assert (z.assigned()[0] == "smush");
1053         assert (x.assigned().length is 0);
1054         z.params(0);
1055 
1056         // conflict x with z
1057         x.conflicts(z);
1058         assert (args.clear().parse ("-xyz") is false);
1059 
1060         // word mode, with prefix elimination
1061         args = new Arguments (null, null);
1062         assert (args.clear().parse ("foo bar wumpus") is false);
1063         assert (args.clear().parse ("foo bar wumpus wombat", true));
1064         assert (args("foo").set);
1065         assert (args("bar").set);
1066         assert (args("wumpus").set);
1067         assert (args("wombat").set);
1068 
1069         // use '/' instead of '-'
1070         args = new Arguments ("/", "/");
1071         assert (args.clear().parse ("/foo /bar /wumpus") is false);
1072         assert (args.clear().parse ("/foo /bar /wumpus /wombat", true));
1073         assert (args("foo").set);
1074         assert (args("bar").set);
1075         assert (args("wumpus").set);
1076         assert (args("wombat").set);
1077 
1078         // use '/' for short and '-' for long
1079         args = new Arguments ("/", "-");
1080         assert (args.clear().parse ("-foo -bar -wumpus -wombat /abc", true));
1081         assert (args("foo").set);
1082         assert (args("bar").set);
1083         assert (args("wumpus").set);
1084         assert (args("wombat").set);
1085         assert (args("a").set);
1086         assert (args("b").set);
1087         assert (args("c").set);
1088 
1089         // "--" makes all subsequent be implicit parameters
1090         args = new Arguments;
1091         version (dashdash)
1092                 {
1093                 args('f').params(0);
1094                 assert (args.parse ("-f -- -bar -wumpus -wombat --abc"));
1095                 assert (args('f').assigned().length is 0);
1096                 assert (args(null).assigned().length is 4);
1097                 }
1098              else
1099                 {
1100                 args('f').params(2);
1101                 assert (args.parse ("-f -- -bar -wumpus -wombat --abc"));
1102                 assert (args('f').assigned().length is 2);
1103                 assert (args(null).assigned().length is 2);
1104                 }
1105         }
1106 }
1107 
1108 /*******************************************************************************
1109       
1110 *******************************************************************************/
1111 
1112 debug (Arguments)
1113 {       
1114         import tango.io.Stdout;
1115 
1116         void main()
1117         {
1118                 char[] crap = "crap";
1119                 auto args = new Arguments;
1120 
1121                 args(null).title("root").params.help("root help");
1122                 args('x').aliased('X').params(0).required.help("x help");
1123                 args('y').defaults("hi").params(2).smush.explicit.help("y help");
1124                 args('a').required.defaults("hi").requires('y').params(1).help("a help");
1125                 args("foobar").params(2).help("foobar help");
1126                 if (! args.parse ("'one =two' -xa=bar -y=ff -yss --foobar=blah1 --foobar barf blah2 -- a b c d e"))
1127                       stdout (args.errors(&stdout.layout.sprint));
1128                 else
1129                    if (args.get('x'))
1130                        args.help ((char[] a, char[] b){Stdout.formatln ("{}{}\n\t{}", args.lp, a, b);});
1131         }
1132 }