├── .gitignore
├── CSharpTest.Net.Commands.nuspec
├── CSharpTest.Net.Commands.sln
├── README.md
├── packages
└── repositories.config
└── src
├── CSharpTest.Net.Commands
├── Argument.cs
├── ArgumentList.cs
├── Attributes.cs
├── CSharpTest.Net.Commands.csproj
├── Command.cs
├── CommandFilter.cs
├── CommandInterpreter.cs
├── DefaultCommands.cs
├── DisplayInfoBase.cs
├── Exceptions.cs
├── HelpDisplay.cs
├── Interfaces.cs
├── LICENSE-2.0.txt
├── Option.cs
└── Properties
│ ├── AssemblyInfo.cs
│ └── Internal.cs
├── CSharpTest.Net.CommandsTest
├── CSharpTest.Net.CommandsTest.csproj
├── TestArgumentList.cs
├── TestCmdInterpreter.cs
└── packages.config
└── Example
├── Commands.cs
├── Example.csproj
├── Main.cs
└── Properties
└── AssemblyInfo.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | _ReSharper.*
2 | obj
3 | bin
4 | Bin
5 | Debug
6 | Release
7 | *.user
8 | *.pdb
9 | *.suo
10 | *.sln
11 | *.Generated.cs
12 | *.cache
13 | *.vspscc
14 | *.log
15 | *.ncb
16 | *.pch
17 | *.sdf
18 | *.zip
19 | *.mdf
20 | *.snk
21 | *.DotSettings
22 | *.nupkg
23 |
24 | packages/*/*
25 | depend/*
--------------------------------------------------------------------------------
/CSharpTest.Net.Commands.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $version$
6 | CSharpTest.Net.Commands
7 | CSharpTest.Net.Commands
8 | Roger Knapp
9 | Roger Knapp
10 | en-US
11 | http://csharptest.net/src/LICENSE-2.0.txt
12 | https://github.com/csharptest/CSharpTest.Net.Commands
13 | http://csharptest.net/favicon.ico
14 | false
15 | Copyright 2008-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
16 |
17 | commandline parse args arguments options command-line command interpreter
18 | CSharpTest.Net.Commands provides a command-line console wrapper around a C# class
19 | See readme at https://github.com/csharptest/CSharpTest.Net.Commands for release notes.
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/CSharpTest.Net.Commands.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpTest.Net.Commands", "src\CSharpTest.Net.Commands\CSharpTest.Net.Commands.csproj", "{7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpTest.Net.CommandsTest", "src\CSharpTest.Net.CommandsTest\CSharpTest.Net.CommandsTest.csproj", "{097601FB-7E62-47DA-8E48-B56C9AD5DF20}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5EE7948B-00F6-4B34-9463-8F6499265E24}"
9 | ProjectSection(SolutionItems) = preProject
10 | CSharpTest.Net.Commands.nuspec = CSharpTest.Net.Commands.nuspec
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "src\Example\Example.csproj", "{E6931B7B-8A57-416E-BFDD-FFAEC7BAC5AE}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {097601FB-7E62-47DA-8E48-B56C9AD5DF20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {097601FB-7E62-47DA-8E48-B56C9AD5DF20}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {097601FB-7E62-47DA-8E48-B56C9AD5DF20}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {097601FB-7E62-47DA-8E48-B56C9AD5DF20}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {E6931B7B-8A57-416E-BFDD-FFAEC7BAC5AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {E6931B7B-8A57-416E-BFDD-FFAEC7BAC5AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {E6931B7B-8A57-416E-BFDD-FFAEC7BAC5AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {E6931B7B-8A57-416E-BFDD-FFAEC7BAC5AE}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CSharpTest.Net.Commands
2 | =======================
3 |
4 | CSharpTest.Net.Commands (moved from https://code.google.com/p/csharptest-net/)
5 |
6 | ## Changes ##
7 |
8 | 2014-03-09 Initial clone and extraction from existing library.
9 |
10 | ## Online Help ##
11 |
12 | See the online help for CSharpTest.Net.Commands.CommandInterpreter
13 | http://help.csharptest.net/?CSharpTest.Net.Library~CSharpTest.Net.Commands.CommandInterpreter_members.html
14 |
15 | ## Usage ##
16 |
17 | The nuget package installs both a reference to the compiled assembly as well as a copy of the source code. This allows users to either embed the source directly (stand-alone) and remove the reference, or to remove the source folder "Commands" and use the referenced library.
18 |
19 | ## Example ##
20 |
21 | The following example program exposes a command-line that supports the "Example" command to print "Hello World" to std::out, and a Help command that describes the commands available. See examples for more uses.
22 |
23 | ```
24 | class Program
25 | {
26 | public static void Example()
27 | {
28 | Console.WriteLine("Hello World");
29 | }
30 |
31 | [STAThread]
32 | static int Main(string[] args)
33 | {
34 | // Construct the CommandInterpreter and initialize
35 | ICommandInterpreter ci = new CommandInterpreter(
36 | DefaultCommands.Help,
37 | typeof(Program)
38 | );
39 |
40 | ci.Run(args);
41 |
42 | return ci.ErrorLevel;
43 | }
44 | }
45 | ```
46 |
--------------------------------------------------------------------------------
/packages/repositories.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/CSharpTest.Net.Commands/Argument.cs:
--------------------------------------------------------------------------------
1 | #region Copyright 2009-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
2 | /* Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | #endregion
15 | using System;
16 | using System.Collections.Generic;
17 | using System.Reflection;
18 | using System.ComponentModel;
19 | using System.Text;
20 |
21 | namespace CSharpTest.Net.Commands
22 | {
23 | [System.Diagnostics.DebuggerDisplay("{Parameter}")]
24 | partial class Argument : DisplayInfoBase, IArgument
25 | {
26 | readonly object _default;
27 | readonly bool _required;
28 | readonly bool _allArguments;
29 |
30 | public Argument(object target, ParameterInfo mi)
31 | : base(target, mi)
32 | {
33 | _default = null;
34 | _required = true;
35 | _allArguments = Parameter.IsDefined(typeof(AllArgumentsAttribute), true);
36 |
37 | if (Parameter.DefaultValue != DBNull.Value)
38 | {
39 | _default = Parameter.DefaultValue;
40 | _required = false;
41 | }
42 |
43 | foreach (DefaultValueAttribute a in mi.GetCustomAttributes(typeof(DefaultValueAttribute), true))
44 | {
45 | _default = a.Value;
46 | _required = false;
47 | }
48 |
49 | foreach (ArgumentAttribute a in mi.GetCustomAttributes(typeof(ArgumentAttribute), true))
50 | {
51 | if (a.HasDefault)
52 | {
53 | _required = false;
54 | _default = a.DefaultValue;
55 | }
56 | }
57 |
58 | if (Visible && _description == mi.ToString())
59 | {
60 | if (IsFlag)
61 | {
62 | if (Required)
63 | _description = String.Format("Required flag can be \"/{0}\" or \"/{0}:false\".", base.DisplayName);
64 | else
65 | _description = String.Format("Optional flag of \"/{0}\" or \"/{0}:false\".", base.DisplayName);
66 | }
67 | else
68 | {
69 | if (Required)
70 | _description = String.Format("Specifies the required value for \"{0}\" of type {1}.", base.DisplayName, UnderlyingType.Name);
71 | else
72 | _description = String.Format("Specifies an optional value for \"{0}\" of type {1}.", base.DisplayName, UnderlyingType.Name);
73 | }
74 | }
75 | }
76 |
77 | private ParameterInfo Parameter { get { return (ParameterInfo)base.Member; } }
78 |
79 | public Type Type { get { return Parameter.ParameterType; } }
80 | Type UnderlyingType { get { return Nullable.GetUnderlyingType(Parameter.ParameterType) ?? Parameter.ParameterType; } }
81 |
82 | public override bool Visible { get { return base.Visible && !IsInterpreter && !IsAllArguments; } }
83 | public bool Required { get { return _required; } }
84 | public bool IsFlag { get { return Parameter.ParameterType == typeof(bool); } }
85 | public bool IsInterpreter { get { return Parameter.ParameterType == typeof(ICommandInterpreter); } }
86 | public bool IsAllArguments { get { return _allArguments; } }
87 | public Object DefaultValue { get { return _default; } }
88 |
89 | internal Object GetArgumentValue(ICommandInterpreter interpreter, ArgumentList args, string[] allArguments)
90 | {
91 | object value = null;
92 |
93 | if (IsInterpreter)
94 | return interpreter;
95 |
96 | if (IsAllArguments)
97 | {
98 | args.Clear();
99 | args.Unnamed.Clear();
100 | return allArguments;
101 | }
102 |
103 | foreach (string name in AllNames)
104 | {
105 | ArgumentList.Item argitem;
106 | if (args.TryGetValue(name, out argitem))
107 | {
108 | if (Parameter.ParameterType == typeof(string[]))
109 | value = argitem.Values;
110 | else if (IsFlag)
111 | {
112 | bool result;
113 | value = (String.IsNullOrEmpty(argitem.Value) || (bool.TryParse(argitem.Value, out result) && result));
114 | }
115 | else
116 | value = argitem.Value;
117 | args.Remove(name);
118 | }
119 | }
120 |
121 | return base.ChangeType(value, Parameter.ParameterType, Required, DefaultValue);
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/CSharpTest.Net.Commands/ArgumentList.cs:
--------------------------------------------------------------------------------
1 | #region Copyright 2008-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
2 | /* Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | #endregion
15 |
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Text.RegularExpressions;
19 | using System.Text;
20 |
21 | namespace CSharpTest.Net.Commands
22 | {
23 | ///
24 | /// This is a private class as the means of sharing is to simply include the source file not
25 | /// reference a library.
26 | ///
27 | [System.Diagnostics.DebuggerNonUserCode]
28 | partial class ArgumentList : System.Collections.ObjectModel.KeyedCollection
29 | {
30 | #region Static Configuration Options
31 | static StringComparer _defaultCompare = StringComparer.OrdinalIgnoreCase;
32 | static char[] _prefix = new char[] { '/', '-' };
33 | static char[] _delim = new char[] { ':', '=' };
34 | static readonly string[] EmptyList = new string[0];
35 |
36 | ///
37 | /// Controls the default string comparer used for this class
38 | ///
39 | public static StringComparer DefaultComparison
40 | {
41 | get { return _defaultCompare; }
42 | set
43 | {
44 | if (value == null) throw new ArgumentNullException();
45 | _defaultCompare = value;
46 | }
47 | }
48 |
49 | ///
50 | /// Controls the allowable prefix characters that will preceed named arguments
51 | ///
52 | public static char[] PrefixChars
53 | {
54 | get { return (char[])_prefix.Clone(); }
55 | set
56 | {
57 | if (value == null) throw new ArgumentNullException();
58 | if (value.Length == 0) throw new ArgumentOutOfRangeException();
59 | _prefix = (char[])value.Clone();
60 | }
61 | }
62 | ///
63 | /// Controls the allowable delimeter characters seperate argument names from values
64 | ///
65 | public static char[] NameDelimeters
66 | {
67 | get { return (char[])_delim.Clone(); }
68 | set
69 | {
70 | if (value == null) throw new ArgumentNullException();
71 | if (value.Length == 0) throw new ArgumentOutOfRangeException();
72 | _delim = (char[])value.Clone();
73 | }
74 | }
75 | #endregion Static Configuration Options
76 |
77 | readonly List _unnamed;
78 | ///
79 | /// Initializes a new instance of the ArgumentList class using the argument list provided
80 | ///
81 | public ArgumentList(params string[] arguments) : this(DefaultComparison, arguments) { }
82 | ///
83 | /// Initializes a new instance of the ArgumentList class using the argument list provided
84 | /// and using the string comparer provided, by default this is case-insensitive
85 | ///
86 | public ArgumentList(StringComparer comparer, params string[] arguments)
87 | : base(comparer, 0)
88 | {
89 | _unnamed = new List();
90 | this.AddRange(arguments);
91 | }
92 |
93 | ///
94 | /// Returns a list of arguments that did not start with a character in the PrefixChars
95 | /// static collection. These arguments can be modified by the methods on the returned
96 | /// collection, or you set this property to a new collection (a copy is made).
97 | ///
98 | public IList Unnamed
99 | {
100 | get { return _unnamed; }
101 | set
102 | {
103 | _unnamed.Clear();
104 | if (value != null)
105 | _unnamed.AddRange(value);
106 | }
107 | }
108 |
109 | ///
110 | /// Parses the strings provided for switch names and optionally values, by default in one
111 | /// of the following forms: "/name=value", "/name:value", "-name=value", "-name:value"
112 | ///
113 | public void AddRange(params string[] arguments)
114 | {
115 | if (arguments == null) throw new ArgumentNullException();
116 |
117 | foreach (string arg in arguments)
118 | {
119 | string name, value;
120 | if (TryParseNameValue(arg, out name, out value))
121 | Add(name, value);
122 | else
123 | _unnamed.Add(CleanArgument(arg));
124 | }
125 | }
126 |
127 | ///
128 | /// Adds a name/value pair to the collection of arguments, if value is null the name is
129 | /// added with no values.
130 | ///
131 | public void Add(string name, string value)
132 | {
133 | if (name == null)
134 | throw new ArgumentNullException();
135 |
136 | Item item;
137 | if (!TryGetValue(name, out item))
138 | base.Add(item = new Item(name));
139 |
140 | if (value != null)
141 | item.Add(value);
142 | }
143 |
144 | ///
145 | /// A string collection of all keys in the arguments
146 | ///
147 | public string[] Keys
148 | {
149 | get
150 | {
151 | if (Dictionary == null) return new string[0];
152 | List list = new List(Dictionary.Keys);
153 | list.Sort();
154 | return list.ToArray();
155 | }
156 | }
157 |
158 | ///
159 | /// Returns true if the value was found by that name and set the output value
160 | ///
161 | public bool TryGetValue(string name, out Item value)
162 | {
163 | if (name == null)
164 | throw new ArgumentNullException();
165 |
166 | if (Dictionary != null)
167 | return Dictionary.TryGetValue(name, out value);
168 | value = null;
169 | return false;
170 | }
171 |
172 | ///
173 | /// Returns true if the value was found by that name and set the output value
174 | ///
175 | public bool TryGetValue(string name, out string value)
176 | {
177 | if (name == null)
178 | throw new ArgumentNullException();
179 |
180 | Item test;
181 | if (Dictionary != null && Dictionary.TryGetValue(name, out test))
182 | {
183 | value = test.Value;
184 | return true;
185 | }
186 | value = null;
187 | return false;
188 | }
189 |
190 | ///
191 | /// Returns an Item of name even if it does not exist
192 | ///
193 | public Item SafeGet(string name)
194 | {
195 | Item result;
196 | if (TryGetValue(name, out result))
197 | return result;
198 | return new Item(name, null);
199 | }
200 |
201 | #region Protected / Private operations...
202 |
203 | static string CleanArgument(string argument)
204 | {
205 | if (argument == null) throw new ArgumentNullException();
206 | if (argument.Length >= 2 && argument[0] == '"' && argument[argument.Length - 1] == '"')
207 | argument = argument.Substring(1, argument.Length - 2).Replace("\"\"", "\"");
208 | return argument;
209 | }
210 |
211 | ///
212 | /// Attempts to parse a name value pair from '/name=value' format
213 | ///
214 | public static bool TryParseNameValue(string argument, out string name, out string value)
215 | {
216 | argument = CleanArgument(argument);//strip quotes
217 | name = value = null;
218 |
219 | if (String.IsNullOrEmpty(argument) || 0 != argument.IndexOfAny(_prefix, 0, 1))
220 | return false;
221 |
222 | name = argument.Substring(1);
223 | if (String.IsNullOrEmpty(name))
224 | return false;
225 |
226 | int endName = name.IndexOfAny(_delim, 1);
227 |
228 | if (endName > 0)
229 | {
230 | value = name.Substring(endName + 1);
231 | name = name.Substring(0, endName);
232 | }
233 |
234 | return true;
235 | }
236 |
237 | ///
238 | /// Searches the arguments until it finds a switch or value by the name in find and
239 | /// if found it will:
240 | /// A) Remove the item from the arguments
241 | /// B) Set the out parameter value to any value found, or null if just '/name'
242 | /// C) Returns true that it was found and removed.
243 | ///
244 | public static bool Remove(ref string[] arguments, string find, out string value)
245 | {
246 | value = null;
247 | for (int i = 0; i < arguments.Length; i++)
248 | {
249 | string name, setting;
250 | if (TryParseNameValue(arguments[i], out name, out setting) &&
251 | _defaultCompare.Equals(name, find))
252 | {
253 | List args = new List(arguments);
254 | args.RemoveAt(i);
255 | arguments = args.ToArray();
256 | value = setting;
257 | return true;
258 | }
259 | }
260 | return false;
261 | }
262 |
263 | ///
264 | /// Abract override for extracting key
265 | ///
266 | protected override string GetKeyForItem(ArgumentList.Item item)
267 | {
268 | return item.Name;
269 | }
270 |
271 | #endregion
272 |
273 | #region Item class used for collection
274 | ///
275 | /// This is a single named argument within an argument list collection, this
276 | /// can be implicitly assigned to a string, or a string[] array
277 | ///
278 | [System.Diagnostics.DebuggerNonUserCode]
279 | public class Item : System.Collections.ObjectModel.Collection
280 | {
281 | private readonly string _name;
282 | private readonly List _values;
283 |
284 | ///
285 | /// Constructs an item for the name and values provided.
286 | ///
287 | public Item(string name, params string[] items)
288 | : this(new List(), name, items) { }
289 |
290 | private Item(List impl, string name, string[] items)
291 | : base(impl)
292 | {
293 | if (name == null)
294 | throw new ArgumentNullException();
295 |
296 | _name = name;
297 | _values = impl;
298 | if (items != null)
299 | _values.AddRange(items);
300 | }
301 |
302 | ///
303 | /// Returns the name of this item
304 | ///
305 | public string Name { get { return _name; } }
306 |
307 | ///
308 | /// Returns the first value of this named item or null if one doesn't exist
309 | ///
310 | public string Value
311 | {
312 | get { return _values.Count > 0 ? _values[0] : null; }
313 | set
314 | {
315 | _values.Clear();
316 | if (value != null)
317 | _values.Add(value);
318 | }
319 | }
320 |
321 | ///
322 | /// Returns the collection of items in this named slot
323 | ///
324 | public string[] Values
325 | {
326 | get { return _values.ToArray(); }
327 | set
328 | {
329 | _values.Clear();
330 | if (value != null)
331 | _values.AddRange(value);
332 | }
333 | }
334 |
335 | ///
336 | /// Same as the .Values property, returns the collection of items in this named slot
337 | ///
338 | ///
339 | public string[] ToArray() { return _values.ToArray(); }
340 | ///
341 | /// Add one or more values to this named item
342 | ///
343 | public void AddRange(IEnumerable items) { _values.AddRange(items); }
344 |
345 | ///
346 | /// Converts this item to key-value pair to rem to a dictionary
347 | ///
348 | public static implicit operator KeyValuePair(Item item)
349 | {
350 | if (item == null) throw new ArgumentNullException();
351 | return new KeyValuePair(item.Name, item.Values);
352 | }
353 |
354 | ///
355 | /// Converts this item to a string by getting the first value or null if none
356 | ///
357 | public static implicit operator string(Item item) { return item == null ? null : item.Value; }
358 |
359 | ///
360 | /// Converts this item to array of strings
361 | ///
362 | public static implicit operator string[](Item item) { return item == null ? null : item.Values; }
363 | }
364 |
365 | #endregion Item class used for collection
366 |
367 | private class ArgReader
368 | {
369 | const char CharEmpty = (char)0;
370 | char[] _chars;
371 | int _pos;
372 | public ArgReader(string data)
373 | {
374 | _chars = data.ToCharArray();
375 | _pos = 0;
376 | }
377 |
378 | public bool MoveNext() { _pos++; return _pos < _chars.Length; }
379 | public char Current { get { return (_pos < _chars.Length) ? _chars[_pos] : CharEmpty; } }
380 | public bool IsWhiteSpace { get { return Char.IsWhiteSpace(Current); } }
381 | public bool IsQuote { get { return (Current == '"'); } }
382 | public bool IsEOF { get { return _pos >= _chars.Length; } }
383 | }
384 |
385 | /// Parses the individual arguments from the given input string.
386 | public static string[] Parse(string rawtext)
387 | {
388 | List list = new List();
389 | if (rawtext == null)
390 | throw new ArgumentNullException("rawtext");
391 | ArgReader characters = new ArgReader(rawtext.Trim());
392 |
393 | while (!characters.IsEOF)
394 | {
395 | if (characters.IsWhiteSpace)
396 | {
397 | characters.MoveNext();
398 | continue;
399 | }
400 |
401 | StringBuilder sb = new StringBuilder();
402 |
403 | if (characters.IsQuote)
404 | {//quoted string
405 | while (characters.MoveNext())
406 | {
407 | if (characters.IsQuote)
408 | {
409 | if (!characters.MoveNext() || characters.IsWhiteSpace)
410 | break;
411 | }
412 | sb.Append(characters.Current);
413 | }
414 | }
415 | else
416 | {
417 | sb.Append(characters.Current);
418 | while (characters.MoveNext())
419 | {
420 | if (characters.IsWhiteSpace)
421 | break;
422 | sb.Append(characters.Current);
423 | }
424 | }
425 |
426 | list.Add(sb.ToString());
427 | }
428 | return list.ToArray();
429 | }
430 |
431 | /// The inverse of Parse, joins the arguments together and properly escapes output
432 | [Obsolete("Consider migrating to EscapeArguments as it correctly escapes some situations that Join does not.")]
433 | public static string Join(params string[] arguments)
434 | {
435 | if (arguments == null)
436 | throw new ArgumentNullException("arguments");
437 | char[] escaped = " \t\"&()[]{}^=;!'+,`~".ToCharArray();
438 |
439 | StringBuilder sb = new StringBuilder();
440 | foreach (string argument in arguments)
441 | {
442 | string arg = argument;
443 |
444 | if( arg.IndexOfAny(escaped) >= 0 )
445 | sb.AppendFormat("\"{0}\"", arg.Replace("\"", "\"\""));
446 | else
447 | sb.Append(arg);
448 |
449 | sb.Append(' ');
450 | }
451 |
452 | return sb.ToString(0, Math.Max(0, sb.Length-1));
453 | }
454 |
455 | /// The 'more' correct escape/join for arguments
456 | public static string EscapeArguments(params string[] args)
457 | {
458 | StringBuilder arguments = new StringBuilder();
459 | Regex invalidChar = new Regex("[\x00\x0a\x0d]");// these can not be escaped
460 | Regex needsQuotes = new Regex(@"\s|""");// contains whitespace or two quote characters
461 | Regex escapeQuote = new Regex(@"(\\*)(""|$)");// one or more '\' followed with a quote or end of string
462 | for (int carg = 0; args != null && carg < args.Length; carg++)
463 | {
464 | if (args[carg] == null) { throw new ArgumentNullException("args[" + carg + "]"); }
465 | if (invalidChar.IsMatch(args[carg])) { throw new ArgumentOutOfRangeException("args[" + carg + "]"); }
466 | if (args[carg] == String.Empty) { arguments.Append("\"\""); }
467 | else if (!needsQuotes.IsMatch(args[carg])) { arguments.Append(args[carg]); }
468 | else
469 | {
470 | arguments.Append('"');
471 | arguments.Append(escapeQuote.Replace(args[carg],
472 | delegate(Match m)
473 | {
474 | return m.Groups[1].Value + m.Groups[1].Value +
475 | (m.Groups[2].Value == "\"" ? "\\\"" : "");
476 | }
477 | ));
478 | arguments.Append('"');
479 | }
480 | if (carg + 1 < args.Length)
481 | arguments.Append(' ');
482 | }
483 | return arguments.ToString();
484 | }
485 | }
486 | }
--------------------------------------------------------------------------------
/src/CSharpTest.Net.Commands/Attributes.cs:
--------------------------------------------------------------------------------
1 | #region Copyright 2009-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
2 | /* Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | #endregion
15 | using System;
16 | using System.Collections.Generic;
17 | using System.ComponentModel;
18 |
19 | namespace CSharpTest.Net.Commands
20 | {
21 | ///
22 | /// Defines an alias name for a command
23 | ///
24 | [Serializable]
25 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
26 | public class AliasNameAttribute : Attribute
27 | {
28 | private readonly string _alias;
29 | /// Constructs an AliasNameAttribute
30 | public AliasNameAttribute(string commandAlias)
31 | {
32 | _alias = commandAlias;
33 | }
34 |
35 | /// Returns the name of the alias
36 | public string Name { get { return _alias; } }
37 | }
38 |
39 | ///
40 | /// Instructs the CommandInterpreter to ignore a specific method/property
41 | ///
42 | [Serializable]
43 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)]
44 | public class IgnoreMemberAttribute : Attribute
45 | {
46 | /// Constructs an IgnoreMemberAttribute
47 | public IgnoreMemberAttribute()
48 | {
49 | }
50 | }
51 | ///
52 | /// Defines that the string[] argument accepts all arguments provided to the command, useage:
53 | /// void MyCommand([AllArguments] string[] arguments)
54 | /// or
55 | /// void MyCommand([AllArguments] string[] arguments, ICommandInterpreter ci)
56 | ///
57 | [Serializable]
58 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
59 | public class AllArgumentsAttribute : Attribute
60 | {
61 | /// Constructs an AllArgumentsAttribute
62 | public AllArgumentsAttribute()
63 | { }
64 | }
65 |
66 | ///
67 | /// Provides all the display properties.
68 | ///
69 | public abstract class DisplayInfoAttribute : Attribute, IDisplayInfo
70 | {
71 | private string _displayName;
72 | private string[] _aliasNames;
73 | private string _category;
74 | private string _description;
75 | private bool _visible;
76 |
77 | /// Constructs the attribute
78 | protected DisplayInfoAttribute(string displayName, params string[] aliasNames)
79 | {
80 | _displayName = displayName;
81 | _aliasNames = aliasNames;
82 | _category = _description = null;
83 | _visible = true;
84 | }
85 |
86 | /// Returns the DisplayName
87 | public string DisplayName { get { return _displayName; } set { _displayName = value; } }
88 | /// Just the alias names
89 | public string[] AliasNames { get { return (string[])_aliasNames.Clone(); } set { _aliasNames = value; } }
90 | /// Returns the name list
91 | public string[] AllNames
92 | {
93 | get
94 | {
95 | List names = new List();
96 | if (_displayName != null) names.Add(_displayName);
97 | names.AddRange(_aliasNames ?? new string[0]);
98 | return names.ToArray();
99 | }
100 | }
101 | /// Returns the Category
102 | public string Category { get { return _category; } set { _category = value; } }
103 | /// Returns the Description
104 | public string Description { get { return _description; } set { _description = value; } }
105 | /// Returns the visibility of the command
106 | public virtual bool Visible { get { return _visible; } set { _visible = value; } }
107 |
108 | Type IDisplayInfo.ReflectedType { get { return null; } }
109 | void IDisplayInfo.AddAttribute(T attribute) { }
110 | bool IDisplayInfo.TryGetAttribute(out T found)
111 | {
112 | found = null;
113 | return false;
114 | }
115 |
116 | void IDisplayInfo.Help() { }
117 | }
118 |
119 | /// Contains display info and a default value
120 | public abstract class DisplayInfoAndValueAttribute : DisplayInfoAttribute
121 | {
122 | private object _defaultValue;
123 | private bool _hasDefault;
124 |
125 | /// Constructs the attribute
126 | protected DisplayInfoAndValueAttribute(string displayName, params string[] aliasNames)
127 | : base(displayName, aliasNames)
128 | { }
129 |
130 | /// Gets/sets the default value for the option
131 | public object DefaultValue { get { return _defaultValue; } set { _hasDefault = true; _defaultValue = value; } }
132 |
133 | /// Returns true if a default value was specified
134 | internal bool HasDefault { get { return _hasDefault; } }
135 | }
136 |
137 | ///
138 | /// Provides all the properties available for a command 'filter' that is
139 | /// called for every command invoked enabling custom processing of arguments
140 | /// and pre/post processing. The attribute is optional, the format of the
141 | /// the method prototype is not and must be:
142 | /// void (ICommandInterpreter interpreter, ICommandChain chain, string[] arguments);
143 | ///
144 | [Serializable]
145 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
146 | public class CommandFilterAttribute : DisplayInfoAttribute
147 | {
148 | char[] _keys;
149 |
150 | /// Constructs the attribute
151 | public CommandFilterAttribute(char key)
152 | : this(new char[] { key})
153 | { }
154 |
155 | /// Constructs the attribute
156 | public CommandFilterAttribute( params char[] keys )
157 | : base(null, new string[0])
158 | {
159 | _keys = keys;
160 | base.Visible = false;
161 | }
162 |
163 | /// Returns the keys associated with this filter
164 | public Char[] Keys { get { return _keys; } }
165 |
166 | /// Ignored.
167 | [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
168 | public override bool Visible { get { return false; } set { } }
169 | }
170 | ///
171 | /// Provides all the properties available for a command.
172 | ///
173 | [Serializable]
174 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
175 | public class CommandAttribute : DisplayInfoAttribute
176 | {
177 | /// Constructs the attribute
178 | public CommandAttribute()
179 | : base(null, new string[0])
180 | { }
181 | /// Constructs the attribute
182 | public CommandAttribute(string displayName)
183 | : base(displayName, new string[0])
184 | { }
185 | /// Constructs the attribute
186 | public CommandAttribute(string displayName, params string[] aliasNames)
187 | : base(displayName, aliasNames)
188 | { }
189 | }
190 | ///
191 | /// Provides all the properties available for an argument.
192 | ///
193 | [Serializable]
194 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
195 | public class ArgumentAttribute : DisplayInfoAndValueAttribute
196 | {
197 | /// Constructs the attribute
198 | public ArgumentAttribute()
199 | : base(null, new string[0])
200 | { }
201 | /// Constructs the attribute
202 | public ArgumentAttribute(string displayName)
203 | : base(displayName, new string[0])
204 | { }
205 | /// Constructs the attribute
206 | public ArgumentAttribute(string displayName, params string[] aliasNames)
207 | : base(displayName, aliasNames)
208 | { }
209 | }
210 | ///
211 | /// Provides all the properties available for an argument.
212 | ///
213 | [Serializable]
214 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
215 | public class OptionAttribute : DisplayInfoAndValueAttribute
216 | {
217 | /// Constructs the attribute
218 | public OptionAttribute()
219 | : base(null, new string[0])
220 | { }
221 | /// Constructs the attribute
222 | public OptionAttribute(string displayName)
223 | : base(displayName, new string[0])
224 | { }
225 | /// Constructs the attribute
226 | public OptionAttribute(string displayName, params string[] aliasNames)
227 | : base(displayName, aliasNames)
228 | { }
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/CSharpTest.Net.Commands/CSharpTest.Net.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {7BD5EDD1-445C-46D1-A0B2-4B68CB51EADB}
9 | Library
10 | Properties
11 | CSharpTest.Net.Commands
12 | CSharpTest.Net.Commands
13 | v2.0
14 | 512
15 |
16 |
17 |
18 | True
19 | pdbonly
20 | false
21 | bin\
22 | DEBUG;TRACE
23 | none
24 | 4
25 | true
26 | bin\CSharpTest.Net.Commands.XML
27 |
28 |
29 | True
30 | pdbonly
31 | true
32 | bin\
33 | TRACE
34 | none
35 | 4
36 | true
37 | bin\CSharpTest.Net.Commands.XML
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | PreserveNewest
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/src/CSharpTest.Net.Commands/Command.cs:
--------------------------------------------------------------------------------
1 | #region Copyright 2009-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
2 | /* Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | #endregion
15 | using System;
16 | using System.Collections.Generic;
17 | using System.Reflection;
18 | using System.Diagnostics;
19 | using System.Text;
20 | using System.Runtime.Serialization.Formatters.Binary;
21 | using System.IO;
22 | using System.Runtime.Serialization;
23 | using System.Threading;
24 |
25 | namespace CSharpTest.Net.Commands
26 | {
27 | [System.Diagnostics.DebuggerDisplay("{Method}")]
28 | partial class Command : DisplayInfoBase, ICommand
29 | {
30 | Dictionary _names;
31 | Argument[] _arguments;
32 |
33 | public static ICommand Make(object target, MethodInfo mi)
34 | {
35 | ICommand cmd;
36 | if (CommandFilter.TryCreate(target, mi, out cmd))
37 | return cmd;
38 | return new Command(target, mi);
39 | }
40 |
41 | protected Command(object target, MethodInfo mi)
42 | : base(target, mi)
43 | {
44 | ParameterInfo[] paramList = mi.GetParameters();
45 |
46 | _names = new Dictionary(StringComparer.OrdinalIgnoreCase);
47 | List tempList = new List();
48 |
49 | foreach (ParameterInfo pi in paramList)
50 | {
51 | Argument arg = new Argument(target, pi);
52 | foreach(string name in arg.AllNames)
53 | _names.Add(name, tempList.Count);
54 | tempList.Add(arg);
55 | }
56 | _arguments = tempList.ToArray();
57 |
58 | if (base.Description == mi.ToString())
59 | {//if no description provided, let's build a better one
60 | StringBuilder sb = new StringBuilder();
61 | sb.AppendFormat("{0} ", base.DisplayName);
62 | foreach(Argument a in tempList)
63 | if(a.Visible)
64 | sb.AppendFormat("{0} ", a.FormatSyntax(a.DisplayName));
65 | _description = sb.ToString(0, sb.Length - 1);
66 | }
67 | }
68 |
69 | public IArgument[] Arguments { get { return (Argument[])_arguments.Clone(); } }
70 |
71 | private MethodInfo Method { get { return (MethodInfo)base.Member; } }
72 |
73 | public virtual void Run(ICommandInterpreter interpreter, string[] arguments)
74 | {
75 | ArgumentList args = new ArgumentList(arguments);
76 |
77 | if (args.Count == 1 && args.Contains("?"))
78 | { Help(); return; }
79 |
80 | //translate ordinal referenced names
81 | Argument last = null;
82 | for (int i = 0; i < _arguments.Length && args.Unnamed.Count > 0; i++)
83 | {
84 | if (_arguments[i].Type == typeof (ICommandInterpreter))
85 | break;
86 | last = _arguments[i];
87 | args.Add(last.DisplayName, args.Unnamed[0]);
88 | args.Unnamed.RemoveAt(0);
89 | }
90 |
91 | if (last != null && args.Unnamed.Count > 0 && last.Type.IsArray)
92 | {
93 | for (int i = 0; i < _arguments.Length && args.Unnamed.Count > 0; i++)
94 | {
95 | args.Add(last.DisplayName, args.Unnamed[0]);
96 | args.Unnamed.RemoveAt(0);
97 | }
98 | }
99 |
100 | List