Initial commit
[yaffs-website] / node_modules / argparse / lib / argument_parser.js
1 /**
2  * class ArgumentParser
3  *
4  * Object for parsing command line strings into js objects.
5  *
6  * Inherited from [[ActionContainer]]
7  **/
8 'use strict';
9
10 var util    = require('util');
11 var format  = require('util').format;
12 var Path    = require('path');
13 var sprintf = require('sprintf-js').sprintf;
14
15 // Constants
16 var c = require('./const');
17
18 var $$ = require('./utils');
19
20 var ActionContainer = require('./action_container');
21
22 // Errors
23 var argumentErrorHelper = require('./argument/error');
24
25 var HelpFormatter = require('./help/formatter');
26
27 var Namespace = require('./namespace');
28
29
30 /**
31  * new ArgumentParser(options)
32  *
33  * Create a new ArgumentParser object.
34  *
35  * ##### Options:
36  * - `prog`  The name of the program (default: Path.basename(process.argv[1]))
37  * - `usage`  A usage message (default: auto-generated from arguments)
38  * - `description`  A description of what the program does
39  * - `epilog`  Text following the argument descriptions
40  * - `parents`  Parsers whose arguments should be copied into this one
41  * - `formatterClass`  HelpFormatter class for printing help messages
42  * - `prefixChars`  Characters that prefix optional arguments
43  * - `fromfilePrefixChars` Characters that prefix files containing additional arguments
44  * - `argumentDefault`  The default value for all arguments
45  * - `addHelp`  Add a -h/-help option
46  * - `conflictHandler`  Specifies how to handle conflicting argument names
47  * - `debug`  Enable debug mode. Argument errors throw exception in
48  *   debug mode and process.exit in normal. Used for development and
49  *   testing (default: false)
50  *
51  * See also [original guide][1]
52  *
53  * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects
54  **/
55 function ArgumentParser(options) {
56   if (!(this instanceof ArgumentParser)) {
57     return new ArgumentParser(options);
58   }
59   var self = this;
60   options = options || {};
61
62   options.description = (options.description || null);
63   options.argumentDefault = (options.argumentDefault || null);
64   options.prefixChars = (options.prefixChars || '-');
65   options.conflictHandler = (options.conflictHandler || 'error');
66   ActionContainer.call(this, options);
67
68   options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp;
69   options.parents = options.parents || [];
70   // default program name
71   options.prog = (options.prog || Path.basename(process.argv[1]));
72   this.prog = options.prog;
73   this.usage = options.usage;
74   this.epilog = options.epilog;
75   this.version = options.version;
76
77   this.debug = (options.debug === true);
78
79   this.formatterClass = (options.formatterClass || HelpFormatter);
80   this.fromfilePrefixChars = options.fromfilePrefixChars || null;
81   this._positionals = this.addArgumentGroup({ title: 'Positional arguments' });
82   this._optionals = this.addArgumentGroup({ title: 'Optional arguments' });
83   this._subparsers = null;
84
85   // register types
86   function FUNCTION_IDENTITY(o) {
87     return o;
88   }
89   this.register('type', 'auto', FUNCTION_IDENTITY);
90   this.register('type', null, FUNCTION_IDENTITY);
91   this.register('type', 'int', function (x) {
92     var result = parseInt(x, 10);
93     if (isNaN(result)) {
94       throw new Error(x + ' is not a valid integer.');
95     }
96     return result;
97   });
98   this.register('type', 'float', function (x) {
99     var result = parseFloat(x);
100     if (isNaN(result)) {
101       throw new Error(x + ' is not a valid float.');
102     }
103     return result;
104   });
105   this.register('type', 'string', function (x) {
106     return '' + x;
107   });
108
109   // add help and version arguments if necessary
110   var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0];
111   if (options.addHelp) {
112     this.addArgument(
113       [ defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help' ],
114       {
115         action: 'help',
116         defaultValue: c.SUPPRESS,
117         help: 'Show this help message and exit.'
118       }
119     );
120   }
121   if (typeof this.version !== 'undefined') {
122     this.addArgument(
123       [ defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version' ],
124       {
125         action: 'version',
126         version: this.version,
127         defaultValue: c.SUPPRESS,
128         help: "Show program's version number and exit."
129       }
130     );
131   }
132
133   // add parent arguments and defaults
134   options.parents.forEach(function (parent) {
135     self._addContainerActions(parent);
136     if (typeof parent._defaults !== 'undefined') {
137       for (var defaultKey in parent._defaults) {
138         if (parent._defaults.hasOwnProperty(defaultKey)) {
139           self._defaults[defaultKey] = parent._defaults[defaultKey];
140         }
141       }
142     }
143   });
144 }
145
146 util.inherits(ArgumentParser, ActionContainer);
147
148 /**
149  * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]]
150  * - options (object): hash of options see [[ActionSubparsers.new]]
151  *
152  * See also [subcommands][1]
153  *
154  * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands
155  **/
156 ArgumentParser.prototype.addSubparsers = function (options) {
157   if (this._subparsers) {
158     this.error('Cannot have multiple subparser arguments.');
159   }
160
161   options = options || {};
162   options.debug = (this.debug === true);
163   options.optionStrings = [];
164   options.parserClass = (options.parserClass || ArgumentParser);
165
166
167   if (!!options.title || !!options.description) {
168
169     this._subparsers = this.addArgumentGroup({
170       title: (options.title || 'subcommands'),
171       description: options.description
172     });
173     delete options.title;
174     delete options.description;
175
176   } else {
177     this._subparsers = this._positionals;
178   }
179
180   // prog defaults to the usage message of this parser, skipping
181   // optional arguments and with no "usage:" prefix
182   if (!options.prog) {
183     var formatter = this._getFormatter();
184     var positionals = this._getPositionalActions();
185     var groups = this._mutuallyExclusiveGroups;
186     formatter.addUsage(this.usage, positionals, groups, '');
187     options.prog = formatter.formatHelp().trim();
188   }
189
190   // create the parsers action and add it to the positionals list
191   var ParsersClass = this._popActionClass(options, 'parsers');
192   var action = new ParsersClass(options);
193   this._subparsers._addAction(action);
194
195   // return the created parsers action
196   return action;
197 };
198
199 ArgumentParser.prototype._addAction = function (action) {
200   if (action.isOptional()) {
201     this._optionals._addAction(action);
202   } else {
203     this._positionals._addAction(action);
204   }
205   return action;
206 };
207
208 ArgumentParser.prototype._getOptionalActions = function () {
209   return this._actions.filter(function (action) {
210     return action.isOptional();
211   });
212 };
213
214 ArgumentParser.prototype._getPositionalActions = function () {
215   return this._actions.filter(function (action) {
216     return action.isPositional();
217   });
218 };
219
220
221 /**
222  * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object
223  * - args (array): input elements
224  * - namespace (Namespace|Object): result object
225  *
226  * Parsed args and throws error if some arguments are not recognized
227  *
228  * See also [original guide][1]
229  *
230  * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method
231  **/
232 ArgumentParser.prototype.parseArgs = function (args, namespace) {
233   var argv;
234   var result = this.parseKnownArgs(args, namespace);
235
236   args = result[0];
237   argv = result[1];
238   if (argv && argv.length > 0) {
239     this.error(
240       format('Unrecognized arguments: %s.', argv.join(' '))
241     );
242   }
243   return args;
244 };
245
246 /**
247  * ArgumentParser#parseKnownArgs(args, namespace) -> array
248  * - args (array): input options
249  * - namespace (Namespace|Object): result object
250  *
251  * Parse known arguments and return tuple of result object
252  * and unknown args
253  *
254  * See also [original guide][1]
255  *
256  * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing
257  **/
258 ArgumentParser.prototype.parseKnownArgs = function (args, namespace) {
259   var self = this;
260
261   // args default to the system args
262   args = args || process.argv.slice(2);
263
264   // default Namespace built from parser defaults
265   namespace = namespace || new Namespace();
266
267   self._actions.forEach(function (action) {
268     if (action.dest !== c.SUPPRESS) {
269       if (!$$.has(namespace, action.dest)) {
270         if (action.defaultValue !== c.SUPPRESS) {
271           var defaultValue = action.defaultValue;
272           if (typeof action.defaultValue === 'string') {
273             defaultValue = self._getValue(action, defaultValue);
274           }
275           namespace[action.dest] = defaultValue;
276         }
277       }
278     }
279   });
280
281   Object.keys(self._defaults).forEach(function (dest) {
282     namespace[dest] = self._defaults[dest];
283   });
284
285   // parse the arguments and exit if there are any errors
286   try {
287     var res = this._parseKnownArgs(args, namespace);
288
289     namespace = res[0];
290     args = res[1];
291     if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) {
292       args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]);
293       delete namespace[c._UNRECOGNIZED_ARGS_ATTR];
294     }
295     return [ namespace, args ];
296   } catch (e) {
297     this.error(e);
298   }
299 };
300
301 ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
302   var self = this;
303
304   var extras = [];
305
306   // replace arg strings that are file references
307   if (this.fromfilePrefixChars !== null) {
308     argStrings = this._readArgsFromFiles(argStrings);
309   }
310   // map all mutually exclusive arguments to the other arguments
311   // they can't occur with
312   // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])'
313   // though I can't conceive of a way in which an action could be a member
314   // of two different mutually exclusive groups.
315
316   function actionHash(action) {
317     // some sort of hashable key for this action
318     // action itself cannot be a key in actionConflicts
319     // I think getName() (join of optionStrings) is unique enough
320     return action.getName();
321   }
322
323   var conflicts, key;
324   var actionConflicts = {};
325
326   this._mutuallyExclusiveGroups.forEach(function (mutexGroup) {
327     mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) {
328       key = actionHash(mutexAction);
329       if (!$$.has(actionConflicts, key)) {
330         actionConflicts[key] = [];
331       }
332       conflicts = actionConflicts[key];
333       conflicts.push.apply(conflicts, groupActions.slice(0, i));
334       conflicts.push.apply(conflicts, groupActions.slice(i + 1));
335     });
336   });
337
338   // find all option indices, and determine the arg_string_pattern
339   // which has an 'O' if there is an option at an index,
340   // an 'A' if there is an argument, or a '-' if there is a '--'
341   var optionStringIndices = {};
342
343   var argStringPatternParts = [];
344
345   argStrings.forEach(function (argString, argStringIndex) {
346     if (argString === '--') {
347       argStringPatternParts.push('-');
348       while (argStringIndex < argStrings.length) {
349         argStringPatternParts.push('A');
350         argStringIndex++;
351       }
352     } else {
353       // otherwise, add the arg to the arg strings
354       // and note the index if it was an option
355       var pattern;
356       var optionTuple = self._parseOptional(argString);
357       if (!optionTuple) {
358         pattern = 'A';
359       } else {
360         optionStringIndices[argStringIndex] = optionTuple;
361         pattern = 'O';
362       }
363       argStringPatternParts.push(pattern);
364     }
365   });
366   var argStringsPattern = argStringPatternParts.join('');
367
368   var seenActions = [];
369   var seenNonDefaultActions = [];
370
371
372   function takeAction(action, argumentStrings, optionString) {
373     seenActions.push(action);
374     var argumentValues = self._getValues(action, argumentStrings);
375
376     // error if this argument is not allowed with other previously
377     // seen arguments, assuming that actions that use the default
378     // value don't really count as "present"
379     if (argumentValues !== action.defaultValue) {
380       seenNonDefaultActions.push(action);
381       if (actionConflicts[actionHash(action)]) {
382         actionConflicts[actionHash(action)].forEach(function (actionConflict) {
383           if (seenNonDefaultActions.indexOf(actionConflict) >= 0) {
384             throw argumentErrorHelper(
385               action,
386               format('Not allowed with argument "%s".', actionConflict.getName())
387             );
388           }
389         });
390       }
391     }
392
393     if (argumentValues !== c.SUPPRESS) {
394       action.call(self, namespace, argumentValues, optionString);
395     }
396   }
397
398   function consumeOptional(startIndex) {
399     // get the optional identified at this index
400     var optionTuple = optionStringIndices[startIndex];
401     var action = optionTuple[0];
402     var optionString = optionTuple[1];
403     var explicitArg = optionTuple[2];
404
405     // identify additional optionals in the same arg string
406     // (e.g. -xyz is the same as -x -y -z if no args are required)
407     var actionTuples = [];
408
409     var args, argCount, start, stop;
410
411     for (;;) {
412       if (!action) {
413         extras.push(argStrings[startIndex]);
414         return startIndex + 1;
415       }
416       if (explicitArg) {
417         argCount = self._matchArgument(action, 'A');
418
419         // if the action is a single-dash option and takes no
420         // arguments, try to parse more single-dash options out
421         // of the tail of the option string
422         var chars = self.prefixChars;
423         if (argCount === 0 && chars.indexOf(optionString[1]) < 0) {
424           actionTuples.push([ action, [], optionString ]);
425           optionString = optionString[0] + explicitArg[0];
426           var newExplicitArg = explicitArg.slice(1) || null;
427           var optionalsMap = self._optionStringActions;
428
429           if (Object.keys(optionalsMap).indexOf(optionString) >= 0) {
430             action = optionalsMap[optionString];
431             explicitArg = newExplicitArg;
432           } else {
433             throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
434           }
435         } else if (argCount === 1) {
436           // if the action expect exactly one argument, we've
437           // successfully matched the option; exit the loop
438           stop = startIndex + 1;
439           args = [ explicitArg ];
440           actionTuples.push([ action, args, optionString ]);
441           break;
442         } else {
443           // error if a double-dash option did not use the
444           // explicit argument
445           throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
446         }
447       } else {
448         // if there is no explicit argument, try to match the
449         // optional's string arguments with the following strings
450         // if successful, exit the loop
451
452         start = startIndex + 1;
453         var selectedPatterns = argStringsPattern.substr(start);
454
455         argCount = self._matchArgument(action, selectedPatterns);
456         stop = start + argCount;
457
458
459         args = argStrings.slice(start, stop);
460
461         actionTuples.push([ action, args, optionString ]);
462         break;
463       }
464
465     }
466
467     // add the Optional to the list and return the index at which
468     // the Optional's string args stopped
469     if (actionTuples.length < 1) {
470       throw new Error('length should be > 0');
471     }
472     for (var i = 0; i < actionTuples.length; i++) {
473       takeAction.apply(self, actionTuples[i]);
474     }
475     return stop;
476   }
477
478   // the list of Positionals left to be parsed; this is modified
479   // by consume_positionals()
480   var positionals = self._getPositionalActions();
481
482   function consumePositionals(startIndex) {
483     // match as many Positionals as possible
484     var selectedPattern = argStringsPattern.substr(startIndex);
485     var argCounts = self._matchArgumentsPartial(positionals, selectedPattern);
486
487     // slice off the appropriate arg strings for each Positional
488     // and add the Positional and its args to the list
489     for (var i = 0; i < positionals.length; i++) {
490       var action = positionals[i];
491       var argCount = argCounts[i];
492       if (typeof argCount === 'undefined') {
493         continue;
494       }
495       var args = argStrings.slice(startIndex, startIndex + argCount);
496
497       startIndex += argCount;
498       takeAction(action, args);
499     }
500
501     // slice off the Positionals that we just parsed and return the
502     // index at which the Positionals' string args stopped
503     positionals = positionals.slice(argCounts.length);
504     return startIndex;
505   }
506
507   // consume Positionals and Optionals alternately, until we have
508   // passed the last option string
509   var startIndex = 0;
510   var position;
511
512   var maxOptionStringIndex = -1;
513
514   Object.keys(optionStringIndices).forEach(function (position) {
515     maxOptionStringIndex = Math.max(maxOptionStringIndex, parseInt(position, 10));
516   });
517
518   var positionalsEndIndex, nextOptionStringIndex;
519
520   while (startIndex <= maxOptionStringIndex) {
521     // consume any Positionals preceding the next option
522     nextOptionStringIndex = null;
523     for (position in optionStringIndices) {
524       if (!optionStringIndices.hasOwnProperty(position)) { continue; }
525
526       position = parseInt(position, 10);
527       if (position >= startIndex) {
528         if (nextOptionStringIndex !== null) {
529           nextOptionStringIndex = Math.min(nextOptionStringIndex, position);
530         } else {
531           nextOptionStringIndex = position;
532         }
533       }
534     }
535
536     if (startIndex !== nextOptionStringIndex) {
537       positionalsEndIndex = consumePositionals(startIndex);
538       // only try to parse the next optional if we didn't consume
539       // the option string during the positionals parsing
540       if (positionalsEndIndex > startIndex) {
541         startIndex = positionalsEndIndex;
542         continue;
543       } else {
544         startIndex = positionalsEndIndex;
545       }
546     }
547
548     // if we consumed all the positionals we could and we're not
549     // at the index of an option string, there were extra arguments
550     if (!optionStringIndices[startIndex]) {
551       var strings = argStrings.slice(startIndex, nextOptionStringIndex);
552       extras = extras.concat(strings);
553       startIndex = nextOptionStringIndex;
554     }
555     // consume the next optional and any arguments for it
556     startIndex = consumeOptional(startIndex);
557   }
558
559   // consume any positionals following the last Optional
560   var stopIndex = consumePositionals(startIndex);
561
562   // if we didn't consume all the argument strings, there were extras
563   extras = extras.concat(argStrings.slice(stopIndex));
564
565   // if we didn't use all the Positional objects, there were too few
566   // arg strings supplied.
567   if (positionals.length > 0) {
568     self.error('too few arguments');
569   }
570
571   // make sure all required actions were present
572   self._actions.forEach(function (action) {
573     if (action.required) {
574       if (seenActions.indexOf(action) < 0) {
575         self.error(format('Argument "%s" is required', action.getName()));
576       }
577     }
578   });
579
580   // make sure all required groups have one option present
581   var actionUsed = false;
582   self._mutuallyExclusiveGroups.forEach(function (group) {
583     if (group.required) {
584       actionUsed = group._groupActions.some(function (action) {
585         return seenNonDefaultActions.indexOf(action) !== -1;
586       });
587
588       // if no actions were used, report the error
589       if (!actionUsed) {
590         var names = [];
591         group._groupActions.forEach(function (action) {
592           if (action.help !== c.SUPPRESS) {
593             names.push(action.getName());
594           }
595         });
596         names = names.join(' ');
597         var msg = 'one of the arguments ' + names + ' is required';
598         self.error(msg);
599       }
600     }
601   });
602
603   // return the updated namespace and the extra arguments
604   return [ namespace, extras ];
605 };
606
607 ArgumentParser.prototype._readArgsFromFiles = function (argStrings) {
608   // expand arguments referencing files
609   var self = this;
610   var fs = require('fs');
611   var newArgStrings = [];
612   argStrings.forEach(function (argString) {
613     if (self.fromfilePrefixChars.indexOf(argString[0]) < 0) {
614       // for regular arguments, just add them back into the list
615       newArgStrings.push(argString);
616     } else {
617       // replace arguments referencing files with the file content
618       try {
619         var argstrs = [];
620         var filename = argString.slice(1);
621         var content = fs.readFileSync(filename, 'utf8');
622         content = content.trim().split('\n');
623         content.forEach(function (argLine) {
624           self.convertArgLineToArgs(argLine).forEach(function (arg) {
625             argstrs.push(arg);
626           });
627           argstrs = self._readArgsFromFiles(argstrs);
628         });
629         newArgStrings.push.apply(newArgStrings, argstrs);
630       } catch (error) {
631         return self.error(error.message);
632       }
633     }
634   });
635   return newArgStrings;
636 };
637
638 ArgumentParser.prototype.convertArgLineToArgs = function (argLine) {
639   return [ argLine ];
640 };
641
642 ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) {
643
644   // match the pattern for this action to the arg strings
645   var regexpNargs = new RegExp('^' + this._getNargsPattern(action));
646   var matches = regexpArgStrings.match(regexpNargs);
647   var message;
648
649   // throw an exception if we weren't able to find a match
650   if (!matches) {
651     switch (action.nargs) {
652       /*eslint-disable no-undefined*/
653       case undefined:
654       case null:
655         message = 'Expected one argument.';
656         break;
657       case c.OPTIONAL:
658         message = 'Expected at most one argument.';
659         break;
660       case c.ONE_OR_MORE:
661         message = 'Expected at least one argument.';
662         break;
663       default:
664         message = 'Expected %s argument(s)';
665     }
666
667     throw argumentErrorHelper(
668       action,
669       format(message, action.nargs)
670     );
671   }
672   // return the number of arguments matched
673   return matches[1].length;
674 };
675
676 ArgumentParser.prototype._matchArgumentsPartial = function (actions, regexpArgStrings) {
677   // progressively shorten the actions list by slicing off the
678   // final actions until we find a match
679   var self = this;
680   var result = [];
681   var actionSlice, pattern, matches;
682   var i, j;
683
684   function getLength(string) {
685     return string.length;
686   }
687
688   for (i = actions.length; i > 0; i--) {
689     pattern = '';
690     actionSlice = actions.slice(0, i);
691     for (j = 0; j < actionSlice.length; j++) {
692       pattern += self._getNargsPattern(actionSlice[j]);
693     }
694
695     pattern = new RegExp('^' + pattern);
696     matches = regexpArgStrings.match(pattern);
697
698     if (matches && matches.length > 0) {
699       // need only groups
700       matches = matches.splice(1);
701       result = result.concat(matches.map(getLength));
702       break;
703     }
704   }
705
706   // return the list of arg string counts
707   return result;
708 };
709
710 ArgumentParser.prototype._parseOptional = function (argString) {
711   var action, optionString, argExplicit, optionTuples;
712
713   // if it's an empty string, it was meant to be a positional
714   if (!argString) {
715     return null;
716   }
717
718   // if it doesn't start with a prefix, it was meant to be positional
719   if (this.prefixChars.indexOf(argString[0]) < 0) {
720     return null;
721   }
722
723   // if the option string is present in the parser, return the action
724   if (this._optionStringActions[argString]) {
725     return [ this._optionStringActions[argString], argString, null ];
726   }
727
728   // if it's just a single character, it was meant to be positional
729   if (argString.length === 1) {
730     return null;
731   }
732
733   // if the option string before the "=" is present, return the action
734   if (argString.indexOf('=') >= 0) {
735     optionString = argString.split('=', 1)[0];
736     argExplicit = argString.slice(optionString.length + 1);
737
738     if (this._optionStringActions[optionString]) {
739       action = this._optionStringActions[optionString];
740       return [ action, optionString, argExplicit ];
741     }
742   }
743
744   // search through all possible prefixes of the option string
745   // and all actions in the parser for possible interpretations
746   optionTuples = this._getOptionTuples(argString);
747
748   // if multiple actions match, the option string was ambiguous
749   if (optionTuples.length > 1) {
750     var optionStrings = optionTuples.map(function (optionTuple) {
751       return optionTuple[1];
752     });
753     this.error(format(
754           'Ambiguous option: "%s" could match %s.',
755           argString, optionStrings.join(', ')
756     ));
757   // if exactly one action matched, this segmentation is good,
758   // so return the parsed action
759   } else if (optionTuples.length === 1) {
760     return optionTuples[0];
761   }
762
763   // if it was not found as an option, but it looks like a negative
764   // number, it was meant to be positional
765   // unless there are negative-number-like options
766   if (argString.match(this._regexpNegativeNumber)) {
767     if (!this._hasNegativeNumberOptionals.some(Boolean)) {
768       return null;
769     }
770   }
771   // if it contains a space, it was meant to be a positional
772   if (argString.search(' ') >= 0) {
773     return null;
774   }
775
776   // it was meant to be an optional but there is no such option
777   // in this parser (though it might be a valid option in a subparser)
778   return [ null, argString, null ];
779 };
780
781 ArgumentParser.prototype._getOptionTuples = function (optionString) {
782   var result = [];
783   var chars = this.prefixChars;
784   var optionPrefix;
785   var argExplicit;
786   var action;
787   var actionOptionString;
788
789   // option strings starting with two prefix characters are only split at
790   // the '='
791   if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) >= 0) {
792     if (optionString.indexOf('=') >= 0) {
793       var optionStringSplit = optionString.split('=', 1);
794
795       optionPrefix = optionStringSplit[0];
796       argExplicit = optionStringSplit[1];
797     } else {
798       optionPrefix = optionString;
799       argExplicit = null;
800     }
801
802     for (actionOptionString in this._optionStringActions) {
803       if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
804         action = this._optionStringActions[actionOptionString];
805         result.push([ action, actionOptionString, argExplicit ]);
806       }
807     }
808
809   // single character options can be concatenated with their arguments
810   // but multiple character options always have to have their argument
811   // separate
812   } else if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) < 0) {
813     optionPrefix = optionString;
814     argExplicit = null;
815     var optionPrefixShort = optionString.substr(0, 2);
816     var argExplicitShort = optionString.substr(2);
817
818     for (actionOptionString in this._optionStringActions) {
819       if (!$$.has(this._optionStringActions, actionOptionString)) continue;
820
821       action = this._optionStringActions[actionOptionString];
822       if (actionOptionString === optionPrefixShort) {
823         result.push([ action, actionOptionString, argExplicitShort ]);
824       } else if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
825         result.push([ action, actionOptionString, argExplicit ]);
826       }
827     }
828
829   // shouldn't ever get here
830   } else {
831     throw new Error(format('Unexpected option string: %s.', optionString));
832   }
833   // return the collected option tuples
834   return result;
835 };
836
837 ArgumentParser.prototype._getNargsPattern = function (action) {
838   // in all examples below, we have to allow for '--' args
839   // which are represented as '-' in the pattern
840   var regexpNargs;
841
842   switch (action.nargs) {
843     // the default (null) is assumed to be a single argument
844     case undefined:
845     case null:
846       regexpNargs = '(-*A-*)';
847       break;
848     // allow zero or more arguments
849     case c.OPTIONAL:
850       regexpNargs = '(-*A?-*)';
851       break;
852     // allow zero or more arguments
853     case c.ZERO_OR_MORE:
854       regexpNargs = '(-*[A-]*)';
855       break;
856     // allow one or more arguments
857     case c.ONE_OR_MORE:
858       regexpNargs = '(-*A[A-]*)';
859       break;
860     // allow any number of options or arguments
861     case c.REMAINDER:
862       regexpNargs = '([-AO]*)';
863       break;
864     // allow one argument followed by any number of options or arguments
865     case c.PARSER:
866       regexpNargs = '(-*A[-AO]*)';
867       break;
868     // all others should be integers
869     default:
870       regexpNargs = '(-*' + $$.repeat('-*A', action.nargs) + '-*)';
871   }
872
873   // if this is an optional action, -- is not allowed
874   if (action.isOptional()) {
875     regexpNargs = regexpNargs.replace(/-\*/g, '');
876     regexpNargs = regexpNargs.replace(/-/g, '');
877   }
878
879   // return the pattern
880   return regexpNargs;
881 };
882
883 //
884 // Value conversion methods
885 //
886
887 ArgumentParser.prototype._getValues = function (action, argStrings) {
888   var self = this;
889
890   // for everything but PARSER args, strip out '--'
891   if (action.nargs !== c.PARSER && action.nargs !== c.REMAINDER) {
892     argStrings = argStrings.filter(function (arrayElement) {
893       return arrayElement !== '--';
894     });
895   }
896
897   var value, argString;
898
899   // optional argument produces a default when not present
900   if (argStrings.length === 0 && action.nargs === c.OPTIONAL) {
901
902     value = (action.isOptional()) ? action.constant : action.defaultValue;
903
904     if (typeof (value) === 'string') {
905       value = this._getValue(action, value);
906       this._checkValue(action, value);
907     }
908
909   // when nargs='*' on a positional, if there were no command-line
910   // args, use the default if it is anything other than None
911   } else if (argStrings.length === 0 && action.nargs === c.ZERO_OR_MORE &&
912     action.optionStrings.length === 0) {
913
914     value = (action.defaultValue || argStrings);
915     this._checkValue(action, value);
916
917   // single argument or optional argument produces a single value
918   } else if (argStrings.length === 1 &&
919         (!action.nargs || action.nargs === c.OPTIONAL)) {
920
921     argString = argStrings[0];
922     value = this._getValue(action, argString);
923     this._checkValue(action, value);
924
925   // REMAINDER arguments convert all values, checking none
926   } else if (action.nargs === c.REMAINDER) {
927     value = argStrings.map(function (v) {
928       return self._getValue(action, v);
929     });
930
931   // PARSER arguments convert all values, but check only the first
932   } else if (action.nargs === c.PARSER) {
933     value = argStrings.map(function (v) {
934       return self._getValue(action, v);
935     });
936     this._checkValue(action, value[0]);
937
938   // all other types of nargs produce a list
939   } else {
940     value = argStrings.map(function (v) {
941       return self._getValue(action, v);
942     });
943     value.forEach(function (v) {
944       self._checkValue(action, v);
945     });
946   }
947
948   // return the converted value
949   return value;
950 };
951
952 ArgumentParser.prototype._getValue = function (action, argString) {
953   var result;
954
955   var typeFunction = this._registryGet('type', action.type, action.type);
956   if (typeof typeFunction !== 'function') {
957     var message = format('%s is not callable', typeFunction);
958     throw argumentErrorHelper(action, message);
959   }
960
961   // convert the value to the appropriate type
962   try {
963     result = typeFunction(argString);
964
965     // ArgumentTypeErrors indicate errors
966     // If action.type is not a registered string, it is a function
967     // Try to deduce its name for inclusion in the error message
968     // Failing that, include the error message it raised.
969   } catch (e) {
970     var name = null;
971     if (typeof action.type === 'string') {
972       name = action.type;
973     } else {
974       name = action.type.name || action.type.displayName || '<function>';
975     }
976     var msg = format('Invalid %s value: %s', name, argString);
977     if (name === '<function>') { msg += '\n' + e.message; }
978     throw argumentErrorHelper(action, msg);
979   }
980   // return the converted value
981   return result;
982 };
983
984 ArgumentParser.prototype._checkValue = function (action, value) {
985   // converted value must be one of the choices (if specified)
986   var choices = action.choices;
987   if (choices) {
988     // choise for argument can by array or string
989     if ((typeof choices === 'string' || Array.isArray(choices)) &&
990         choices.indexOf(value) !== -1) {
991       return;
992     }
993     // choise for subparsers can by only hash
994     if (typeof choices === 'object' && !Array.isArray(choices) && choices[value]) {
995       return;
996     }
997
998     if (typeof choices === 'string') {
999       choices = choices.split('').join(', ');
1000     } else if (Array.isArray(choices)) {
1001       choices =  choices.join(', ');
1002     } else {
1003       choices =  Object.keys(choices).join(', ');
1004     }
1005     var message = format('Invalid choice: %s (choose from [%s])', value, choices);
1006     throw argumentErrorHelper(action, message);
1007   }
1008 };
1009
1010 //
1011 // Help formatting methods
1012 //
1013
1014 /**
1015  * ArgumentParser#formatUsage -> string
1016  *
1017  * Return usage string
1018  *
1019  * See also [original guide][1]
1020  *
1021  * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
1022  **/
1023 ArgumentParser.prototype.formatUsage = function () {
1024   var formatter = this._getFormatter();
1025   formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
1026   return formatter.formatHelp();
1027 };
1028
1029 /**
1030  * ArgumentParser#formatHelp -> string
1031  *
1032  * Return help
1033  *
1034  * See also [original guide][1]
1035  *
1036  * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
1037  **/
1038 ArgumentParser.prototype.formatHelp = function () {
1039   var formatter = this._getFormatter();
1040
1041   // usage
1042   formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
1043
1044   // description
1045   formatter.addText(this.description);
1046
1047   // positionals, optionals and user-defined groups
1048   this._actionGroups.forEach(function (actionGroup) {
1049     formatter.startSection(actionGroup.title);
1050     formatter.addText(actionGroup.description);
1051     formatter.addArguments(actionGroup._groupActions);
1052     formatter.endSection();
1053   });
1054
1055   // epilog
1056   formatter.addText(this.epilog);
1057
1058   // determine help from format above
1059   return formatter.formatHelp();
1060 };
1061
1062 ArgumentParser.prototype._getFormatter = function () {
1063   var FormatterClass = this.formatterClass;
1064   var formatter = new FormatterClass({ prog: this.prog });
1065   return formatter;
1066 };
1067
1068 //
1069 //  Print functions
1070 //
1071
1072 /**
1073  * ArgumentParser#printUsage() -> Void
1074  *
1075  * Print usage
1076  *
1077  * See also [original guide][1]
1078  *
1079  * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
1080  **/
1081 ArgumentParser.prototype.printUsage = function () {
1082   this._printMessage(this.formatUsage());
1083 };
1084
1085 /**
1086  * ArgumentParser#printHelp() -> Void
1087  *
1088  * Print help
1089  *
1090  * See also [original guide][1]
1091  *
1092  * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
1093  **/
1094 ArgumentParser.prototype.printHelp = function () {
1095   this._printMessage(this.formatHelp());
1096 };
1097
1098 ArgumentParser.prototype._printMessage = function (message, stream) {
1099   if (!stream) {
1100     stream = process.stdout;
1101   }
1102   if (message) {
1103     stream.write('' + message);
1104   }
1105 };
1106
1107 //
1108 //  Exit functions
1109 //
1110
1111 /**
1112  * ArgumentParser#exit(status=0, message) -> Void
1113  * - status (int): exit status
1114  * - message (string): message
1115  *
1116  * Print message in stderr/stdout and exit program
1117  **/
1118 ArgumentParser.prototype.exit = function (status, message) {
1119   if (message) {
1120     if (status === 0) {
1121       this._printMessage(message);
1122     } else {
1123       this._printMessage(message, process.stderr);
1124     }
1125   }
1126
1127   process.exit(status);
1128 };
1129
1130 /**
1131  * ArgumentParser#error(message) -> Void
1132  * - err (Error|string): message
1133  *
1134  * Error method Prints a usage message incorporating the message to stderr and
1135  * exits. If you override this in a subclass,
1136  * it should not return -- it should
1137  * either exit or throw an exception.
1138  *
1139  **/
1140 ArgumentParser.prototype.error = function (err) {
1141   var message;
1142   if (err instanceof Error) {
1143     if (this.debug === true) {
1144       throw err;
1145     }
1146     message = err.message;
1147   } else {
1148     message = err;
1149   }
1150   var msg = format('%s: error: %s', this.prog, message) + c.EOL;
1151
1152   if (this.debug === true) {
1153     throw new Error(msg);
1154   }
1155
1156   this.printUsage(process.stderr);
1157
1158   return this.exit(2, msg);
1159 };
1160
1161 module.exports = ArgumentParser;