/* Copyright (c) 2002 Matt Griffith Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections; using System.Threading; namespace NArgs { /// /// The abstract base class for exceptions that the command-line parser can throw. /// public abstract class OptionException : ApplicationException { /// /// Initializes a new OptionException with the given error message. /// /// The error message. public OptionException(string message) : base(message) { } } /// /// Thrown when the command-line contains an option that is not /// recognised. /// /// The Message property contains /// an error string suitable for reporting the error to the user (in /// English). public class UnknownOptionException : OptionException { private string _optionName; /// /// Initializes a new UnknownOptionException. /// /// The name of the unknown option. The name is used to /// generate an error message. public UnknownOptionException( string optionName ) : base("Unknown option '" + optionName + "'") { this._optionName = optionName; } /// /// Gets the name of the unknown option. /// public string OptionName { get { return this._optionName; } } } /// /// DuplicateOptionException is thrown when a duplicate option is added to a CmdLineParser /// instance. /// /// Every option added to a CmdLineParser must have a unique long form. Every /// option with a non-null or non-empty short form, must have a unique short form. /// The Message property contains an error string suitable for reporting the error /// to the user (in English). public class DuplicateOptionException : OptionException { private string _optionName; /// /// Initializes a new DuplicateOptionException. /// /// The duplicate option name. The name is used to /// generate an error message. public DuplicateOptionException( string optionName ) : base("Option '" + optionName + "' already exists. Please provide a unique name for each " + "option.") { this._optionName = optionName; } /// /// Gets the duplicate option name. /// public string OptionName { get { return this._optionName; } } } /// /// Thrown when an illegal or missing value is given by the user for /// an option that requires a value. /// /// The Message property contains /// an error string suitable for reporting the error to the user (in /// English). public class IllegalOptionValueException : OptionException { /// /// Stores the option that caused the error. /// private Option _option; /// /// Stores the illegal value supplied by the user. /// private string _value; /// /// Initializes a new IllegalOptionValueException. /// /// The option with an invalid or missing value. /// The invalid value supplied by the user. public IllegalOptionValueException( Option option, string value ) : base("Illegal value '" + value + "' for option " + "--" + option.LongForm) { this._option = option; this._value = value; } /// /// Gets the option whose value was illegal. /// public Option Option { get { return this._option; } } /// /// Gets the illegal value that the user provided. /// public string Value { get { return this._value; } } } /// /// The abstract base class for all command-line options. /// public abstract class Option { /// /// Stores the short form for the option. /// private readonly string _shortForm; /// /// Stores the long form for the option. /// private readonly string _longForm; /// /// Stores a value indicating whether this option requires a value or not. /// private readonly bool _requiresValue; /// /// Stores the default value for this option. /// private readonly object _defaultValue = null; /// /// Stores the value for this option. /// private object _value = null; /// /// Stores the format provider used to parse values for this option. /// private readonly IFormatProvider _formatProvider; /// /// Initializes a new Option instance. /// /// The optional short form for this command-line option. /// The long form for this command-line option. /// Indicates whether this option requires a value. /// The default value for this option. /// The FormatProvider used to parse values for this option. protected Option(string shortForm, string longForm, bool requiresValue, object defaultValue, IFormatProvider provider) { if ( longForm == null ) throw new ArgumentNullException("longForm", "The long form is not optional."); this._shortForm = shortForm; this._longForm = longForm; this._requiresValue = requiresValue; this._defaultValue = defaultValue; this._formatProvider = provider; } /// /// Gets the short form for this option. /// public string ShortForm { get { return this._shortForm; } } /// /// Gets the long form for this option. /// public string LongForm { get { return this._longForm; } } /// /// Gets a value indicating whether this option requires a value. /// public bool RequiresValue { get { return this._requiresValue; } } /// /// Gets the default value for this option. This is a generic implementation and derived /// types should consider overriding it so users of the type won’t have to cast to the /// appropriate type before using the default value. /// protected virtual object DefaultValue { get { return this._defaultValue; } } /// /// Gets true if this option was missing from the command-line. Otherwise returns false. /// public bool IsMissing { get { return (null == this._value); } } /// /// Gets the Format Provider used to parse values for this option. /// public IFormatProvider FormatProvider { get { return this._formatProvider; } } /* /// /// Parses the given argument for this option. /// /// The argument supplied for this /// public object GetValue( string argument ) { } */ /// /// Sets the value for this option. /// /// The value the user provided for this option. /// The value is null but /// the option requires a value. /// The value is not valid /// for the current option type. public void SetValue(string argument) { if ( this._requiresValue ) { if ( null == argument ) throw new IllegalOptionValueException(this, argument); this._value = this.ParseValue(argument); } else { this._value = true; } } /// /// Gets the value for this option. /// public object Value { get { return this._value; } } /// /// When overridden by a derived class ParseValue extracts and converts an option value /// passed on the command-line. /// /// The option. /// The option value. /// The option has /// an illegal or missing value. protected virtual object ParseValue( string argument ) { return null; } } /// /// Represents a simple switch option. Switch options do not have values. The option’s /// presence or absence is all that is important. /// public class BooleanOption : Option { public BooleanOption(string shortForm, string longForm) : base(shortForm, longForm, false, false, null) {} public BooleanOption(string shortForm, string longForm, bool defaultValue) : base(shortForm, longForm, false, defaultValue, null) {} /// /// Gets the default value for this option. The default value is returned by the Value /// property when IsMissing is true. /// public new bool DefaultValue { get { return (bool)base.DefaultValue; } } /// /// Gets the value for this option. Returns the DefaultValue if IsMissing equals true. /// public new bool Value { get { if(null != base.Value) return (bool)base.Value; else return this.DefaultValue; } } } /// /// Represents an option which requires an integer value. /// public class IntegerOption : Option { /// /// Initializes a new IntegerOption.The default value will be equal to 0 and the /// FormatProvider will equal /// . /// /// The optional short form for this option. /// The long form for this option. public IntegerOption( string shortForm, string longForm ) : base(shortForm, longForm, true, 0, Thread.CurrentThread.CurrentCulture) { } /// /// Initializes a new IntegerOption. Uses the specified default value. /// /// The optional short form for this option. /// The long form for this option. /// The default value returned if the option is missing. /// The IFormatProvider to use when parsing values for this option. public IntegerOption( string shortForm, string longForm, int defaultValue, IFormatProvider provider) : base(shortForm, longForm, true, defaultValue, provider) { } protected override object ParseValue(string argument) { int result; try { result = int.Parse(argument, this.FormatProvider); return result; } catch { throw new IllegalOptionValueException(this, argument); } } /// /// Gets the default value for this option. The default value is returned by the Value /// property when IsMissing is true. /// public new int DefaultValue { get { return (int)base.DefaultValue; } } /// /// Gets the value for this option. Returns the DefaultValue if IsMissing equals true. /// public new int Value { get { if(null != base.Value) return (int)base.Value; else return this.DefaultValue; } } } /// /// Represents an option which requires a floating-point value. /// public class DoubleOption : Option { /// /// Initializes a new DoubleOption. The default value will be equal to /// 0.0 and the FormatProvider will equal /// . /// /// The optional short form for this option. /// The long form for this option. public DoubleOption( string shortForm, string longForm ) : base(shortForm, longForm, true, 0.0, Thread.CurrentThread.CurrentCulture) {} /// /// Initializes a new DoubleOption using the default value provided. /// /// The optional short form for this option. /// The long form for this option. /// The Default Value returned when the user does not provide a value /// for this option. /// The IFormatProvider to use when parsing values for this option. public DoubleOption( string shortForm, string longForm, double defaultValue, IFormatProvider provider) : base(shortForm, longForm, true, defaultValue, provider) {} protected override object ParseValue( string argument ) { double result; try { result = double.Parse(argument, this.FormatProvider); return result; } catch { throw new IllegalOptionValueException(this, argument); } } /// /// Gets the default value for this option. The default value is returned by the Value /// property when IsMissing is true. /// public new double DefaultValue { get { return (double)base.DefaultValue; } } /// /// Gets the value for this option. Returns the DefaultValue if IsMissing equals true. /// public new double Value { get { if(null != base.Value) return (double)base.Value; else return this.DefaultValue; } } } /// /// Represents an option which requires a date value. /// public class DateOption : Option { /// /// Initializes a new DateOption. The default value will be equal to /// and the FormatProvider will equal /// . /// /// The optional short form for this option. /// The long form for this option. public DateOption( string shortForm, string longForm ) : base(shortForm, longForm, true, DateTime.MinValue, Thread.CurrentThread.CurrentCulture) {} /// /// Initializes a new DateOption using the specified DefaultValue and IFormatProvider. /// /// The optional short form for this option. /// The long form for this option. /// The Default Value returned when the user does not provide a value /// for this option. /// The IFormatProvider to use when parsing values for this option. public DateOption( string shortForm, string longForm, DateTime defaultValue, IFormatProvider provider) : base(shortForm, longForm, true, defaultValue, provider) {} protected override object ParseValue( string argument ) { DateTime result; try { result = DateTime.Parse(argument, this.FormatProvider); return result; } catch { throw new IllegalOptionValueException(this, argument); } } /// /// Gets the default value for this option. The default value is returned by the Value /// property when IsMissing is true. /// public new DateTime DefaultValue { get { return (DateTime)base.DefaultValue; } } /// /// Gets the value for this option. Returns the DefaultValue if IsMissing equals true. /// public new DateTime Value { get { if(null != base.Value) return (DateTime)base.Value; else return this.DefaultValue; } } } /// /// Represents an option which requires a string value. /// public class StringOption : Option { /// /// Initializes a new StringOption using for /// the default value. /// /// The optional short form for this option. /// The long form for this option. public StringOption( string shortForm, string longForm ) : base(shortForm, longForm, true, string.Empty, null) {} /// /// Initializes a new StringOption using the default value provided. /// /// The optional short form for this option. /// The long form for this option. /// The Default Value returned when the user does not provide a value /// for this option. public StringOption( string shortForm, string longForm, string defaultValue ) : base(shortForm, longForm, true, defaultValue, null) {} protected override object ParseValue( string argument ) { return argument; } /// /// Gets the default value for this option. The default value is returned by the Value /// property when IsMissing is true. /// public new string DefaultValue { get { return base.DefaultValue as string; } } /// /// Gets the value for this option. Returns the DefaultValue if IsMissing equals true. /// public new string Value { get { if(null != base.Value) return base.Value as string; else return this.DefaultValue; } } } public class CmdLineParser { private string[] _remainingArgs = null; private Hashtable _options = new Hashtable(10); //private Hashtable _values = new Hashtable(10); /// /// Add the given Option to the list of accepted options. /// /// The option to add. /// Returns the option after adding it. /// The short form /// is not unique. /// The long form /// is not unique. /// Every option added to a CmdLineParser must have a unique long form. Every /// option with a non-null or non-empty short form, must have a unique short form. /// The Message property contains an error string suitable for reporting the error /// to the user (in English). public Option AddOption( Option option ) { string shortForm = option.ShortForm; string fullShortForm = "-" + shortForm; string longForm = option.LongForm; string fullLongForm = "--" + longForm; #region Validate Parameters if(null != shortForm && string.Empty != shortForm) if(this._options.ContainsKey(fullShortForm)) throw new DuplicateOptionException(shortForm); if(this._options.ContainsKey(fullLongForm)) throw new DuplicateOptionException(longForm); #endregion if(null != shortForm && string.Empty != shortForm) this._options.Add(fullShortForm, option); this._options.Add(fullLongForm, option); return option; } /// /// Creates a new StringOption and adds it to the list of accepted options. /// /// The optional short form for the option. /// The long form for the option. /// The newly created Option. /// The long form is null. public StringOption AddStringOption( string shortForm, string longForm ) { if(null == longForm) throw new ArgumentNullException("longForm", "The long form is not optional."); StringOption option = new StringOption(shortForm, longForm); AddOption((Option)option); return option; } /// /// Creates a new IntegerOption and adds it to the list of accepted options. /// /// The optional short form for the option. /// The long form for the option. /// The newly created Option. /// The long form is null. public IntegerOption AddIntegerOption( string shortForm, string longForm ) { if(null == longForm) throw new ArgumentNullException("longForm", "The long form is not optional."); IntegerOption option = new IntegerOption(shortForm, longForm); AddOption((Option)option); return option; } /// /// Creates a new DoubleOption and adds it to the list of accepted options. /// /// The optional short form for the option. /// The long form for the option. /// The newly created Option. /// The long form is null. public DoubleOption AddDoubleOption( string shortForm, string longForm ) { if(null == longForm) throw new ArgumentNullException("longForm", "The long form is not optional."); DoubleOption option = new DoubleOption(shortForm, longForm); AddOption((Option)option); return option; } /// /// Creates a new DateOption and adds it to the list of accepted options. /// /// The optional short form for the option. /// The long form for the option. /// The newly created Option. /// The long form is null. public DateOption AddDateOption( string shortForm, string longForm ) { if(null == longForm) throw new ArgumentNullException("longForm", "The long form is not optional."); DateOption option = new DateOption(shortForm, longForm); AddOption((Option)option); return option; } /// /// Creates a new BooleanOption and adds it to the list of accepted options. /// /// The optional short form for the option. /// The long form for the option. /// The newly created Option. /// The long form is null. public BooleanOption AddBooleanOption( string shortForm, string longForm ) { if(null == longForm) throw new ArgumentNullException("longForm", "The long form is not optional."); BooleanOption option = new BooleanOption(shortForm, longForm); AddOption((Option)option); return option; } /* /// /// Returns the value that the user supplied for the given option. /// /// The option to return the value of. /// The value for the given option. public object GetOptionValue( Option option ) { // return this._values[option.LongForm]; } */ /// /// Gets the non-option arguments that the user provided. /// /// An array of the remain user supplied arguments. public string[] GetRemainingArgs() { return this._remainingArgs; } /// /// Extracts the options and non-option arguments from the given /// list of command-line arguments. Stops parsing at the first -- or non-option. /// /// The command-line arguments. /// An option that /// requires a value has a /// An unknown option is /// encountered. public void Parse( string[] args ) //throws IllegalOptionValueException, UnknownOptionException { ArrayList otherArgs = new ArrayList(); int position = 0; while ( position < args.Length) { string currentArgument = args[position]; if ( currentArgument.StartsWith("-") ) { if ( currentArgument == "--" ) { // end of options position += 1; break; } string valueArgument = null; if ( currentArgument.StartsWith("--") ) { // handle --arg=value int equalsPosition = currentArgument.IndexOf("="); if ( equalsPosition != -1 ) { valueArgument = currentArgument.Substring(equalsPosition + 1); currentArgument = currentArgument.Substring(0, equalsPosition); } } Option option = this._options[currentArgument] as Option; if ( option == null ) { throw new UnknownOptionException(currentArgument); } //object value = null; if ( option.RequiresValue ) { if ( valueArgument == null || valueArgument == string.Empty) { position += 1; valueArgument = null; if ( position < args.Length) { valueArgument = args[position]; } } option.SetValue(valueArgument); //value = option.Value; } else { option.SetValue(null); //value = option.Value; } //this._values.Add(option.LongForm, value); position += 1; } // currentArgument.StartsWith("-") else { // We encountered a non-option argument. Quit parsing. break; } } for ( ; position < args.Length; ++position ) { otherArgs.Add(args[position]); } int count = otherArgs.Count; this._remainingArgs = new string[count]; for (int i = 0; i < count; i++) { this._remainingArgs[i] = (string)otherArgs[i]; } } } }