using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace NDesk.Options { public class OptionSet : KeyedCollection { private sealed class ActionOption : Option { private Action action; public ActionOption(string prototype, string description, int count, Action action) : base(prototype, description, count) { if (action == null) { throw new ArgumentNullException("action"); } this.action = action; } protected override void OnParseComplete(OptionContext c) { action(c.OptionValues); } } private sealed class ActionOption : Option { private Action action; public ActionOption(string prototype, string description, Action action) : base(prototype, description, 1) { if (action == null) { throw new ArgumentNullException("action"); } this.action = action; } protected override void OnParseComplete(OptionContext c) { action(Option.Parse(c.OptionValues[0], c)); } } private sealed class ActionOption : Option { private OptionAction action; public ActionOption(string prototype, string description, OptionAction action) : base(prototype, description, 2) { if (action == null) { throw new ArgumentNullException("action"); } this.action = action; } protected override void OnParseComplete(OptionContext c) { action(Option.Parse(c.OptionValues[0], c), Option.Parse(c.OptionValues[1], c)); } } private const int OptionWidth = 29; private Converter localizer; private readonly Regex ValueOption = new Regex("^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); public Converter MessageLocalizer { get { return localizer; } } public OptionSet() : this((string f) => f) { } public OptionSet(Converter localizer) { this.localizer = localizer; } protected override string GetKeyForItem(Option item) { if (item == null) { throw new ArgumentNullException("option"); } if (item.Names != null && item.Names.Length > 0) { return item.Names[0]; } throw new InvalidOperationException("Option has no names!"); } [Obsolete("Use KeyedCollection.this[string]")] protected Option GetOptionForName(string option) { if (option == null) { throw new ArgumentNullException("option"); } try { return base[option]; } catch (KeyNotFoundException) { return null; } } protected override void InsertItem(int index, Option item) { base.InsertItem(index, item); AddImpl(item); } protected override void RemoveItem(int index) { base.RemoveItem(index); Option option = base.Items[index]; for (int i = 1; i < option.Names.Length; i++) { base.Dictionary.Remove(option.Names[i]); } } protected override void SetItem(int index, Option item) { base.SetItem(index, item); RemoveItem(index); AddImpl(item); } private void AddImpl(Option option) { if (option == null) { throw new ArgumentNullException("option"); } List list = new List(option.Names.Length); try { for (int i = 1; i < option.Names.Length; i++) { base.Dictionary.Add(option.Names[i], option); list.Add(option.Names[i]); } } catch (Exception) { foreach (string item in list) { base.Dictionary.Remove(item); } throw; } } public new OptionSet Add(Option option) { base.Add(option); return this; } public OptionSet Add(string prototype, Action action) { return Add(prototype, null, action); } public OptionSet Add(string prototype, string description, Action action) { if (action == null) { throw new ArgumentNullException("action"); } Option item = new ActionOption(prototype, description, 1, delegate(OptionValueCollection v) { action(v[0]); }); base.Add(item); return this; } public OptionSet Add(string prototype, OptionAction action) { return Add(prototype, null, action); } public OptionSet Add(string prototype, string description, OptionAction action) { if (action == null) { throw new ArgumentNullException("action"); } Option item = new ActionOption(prototype, description, 2, delegate(OptionValueCollection v) { action(v[0], v[1]); }); base.Add(item); return this; } public OptionSet Add(string prototype, Action action) { return Add(prototype, null, action); } public OptionSet Add(string prototype, string description, Action action) { return Add(new ActionOption(prototype, description, action)); } public OptionSet Add(string prototype, OptionAction action) { return Add(prototype, null, action); } public OptionSet Add(string prototype, string description, OptionAction action) { return Add(new ActionOption(prototype, description, action)); } protected virtual OptionContext CreateOptionContext() { return new OptionContext(this); } public List Parse(IEnumerable arguments) { OptionContext optionContext = CreateOptionContext(); optionContext.OptionIndex = -1; bool flag = true; List list = new List(); Option def = Contains("<>") ? base["<>"] : null; foreach (string argument in arguments) { optionContext.OptionIndex++; if (argument == "--") { flag = false; } else if (!flag) { Unprocessed(list, def, optionContext, argument); } else if (!Parse(argument, optionContext)) { Unprocessed(list, def, optionContext, argument); } } if (optionContext.Option != null) { optionContext.Option.Invoke(optionContext); } return list; } private static bool Unprocessed(ICollection extra, Option def, OptionContext c, string argument) { if (def == null) { extra.Add(argument); return false; } c.OptionValues.Add(argument); c.Option = def; c.Option.Invoke(c); return false; } protected bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value) { if (argument == null) { throw new ArgumentNullException("argument"); } flag = (name = (sep = (value = null))); Match match = ValueOption.Match(argument); if (!match.Success) { return false; } flag = match.Groups["flag"].Value; name = match.Groups["name"].Value; if (match.Groups["sep"].Success && match.Groups["value"].Success) { sep = match.Groups["sep"].Value; value = match.Groups["value"].Value; } return true; } protected virtual bool Parse(string argument, OptionContext c) { if (c.Option != null) { ParseValue(argument, c); return true; } string flag; string name; string sep; string value; if (!GetOptionParts(argument, out flag, out name, out sep, out value)) { return false; } if (Contains(name)) { Option option = base[name]; c.OptionName = flag + name; c.Option = option; switch (option.OptionValueType) { case OptionValueType.None: c.OptionValues.Add(name); c.Option.Invoke(c); break; case OptionValueType.Optional: case OptionValueType.Required: ParseValue(value, c); break; } return true; } if (ParseBool(argument, name, c)) { return true; } if (ParseBundledValue(flag, string.Concat(name + sep + value), c)) { return true; } return false; } private void ParseValue(string option, OptionContext c) { if (option != null) { string[] array = (c.Option.ValueSeparators != null) ? option.Split(c.Option.ValueSeparators, StringSplitOptions.None) : new string[1] { option }; foreach (string item in array) { c.OptionValues.Add(item); } } if (c.OptionValues.Count == c.Option.MaxValueCount || c.Option.OptionValueType == OptionValueType.Optional) { c.Option.Invoke(c); } else if (c.OptionValues.Count > c.Option.MaxValueCount) { throw new OptionException(localizer(string.Format("Error: Found {0} option values when expecting {1}.", c.OptionValues.Count, c.Option.MaxValueCount)), c.OptionName); } } private bool ParseBool(string option, string n, OptionContext c) { string key; if (n.Length >= 1 && (n[n.Length - 1] == '+' || n[n.Length - 1] == '-') && Contains(key = n.Substring(0, n.Length - 1))) { Option option2 = base[key]; string item = (n[n.Length - 1] == '+') ? option : null; c.OptionName = option; c.Option = option2; c.OptionValues.Add(item); option2.Invoke(c); return true; } return false; } private bool ParseBundledValue(string f, string n, OptionContext c) { if (f != "-") { return false; } for (int i = 0; i < n.Length; i++) { string text = f + n[i].ToString(); string key = n[i].ToString(); if (!Contains(key)) { if (i == 0) { return false; } throw new OptionException(string.Format(localizer("Cannot bundle unregistered option '{0}'."), text), text); } Option option = base[key]; switch (option.OptionValueType) { case OptionValueType.None: break; case OptionValueType.Optional: case OptionValueType.Required: { string text2 = n.Substring(i + 1); c.Option = option; c.OptionName = text; ParseValue((text2.Length != 0) ? text2 : null, c); return true; } default: throw new InvalidOperationException("Unknown OptionValueType: " + option.OptionValueType); } Invoke(c, text, n, option); } return true; } private static void Invoke(OptionContext c, string name, string value, Option option) { c.OptionName = name; c.Option = option; c.OptionValues.Add(value); option.Invoke(c); } public void WriteOptionDescriptions(TextWriter o) { using (IEnumerator