]>
Commit | Line | Data |
---|---|---|
38eb3242 | 1 | diff -u -Nr docky-2.0.7/configure.ac docky-2.0.7.build/configure.ac |
2 | --- docky-2.0.7/configure.ac 2010-08-31 23:28:08.847531997 -0700 | |
3 | +++ docky-2.0.7.build/configure.ac 2010-10-19 01:54:40.136682364 -0700 | |
4 | @@ -76,7 +76,6 @@ | |
5 | SHAMROCK_EXPAND_BINDIR | |
6 | SHAMROCK_EXPAND_DATADIR | |
7 | ||
8 | - | |
9 | PKG_CHECK_MODULES([GCONF_SHARP_20], [gconf-sharp-2.0]) | |
10 | PKG_CHECK_MODULES([GLADE_SHARP_20], [glade-sharp-2.0]) | |
11 | PKG_CHECK_MODULES([GLIB_SHARP_20], [glib-sharp-2.0]) | |
12 | @@ -90,7 +89,6 @@ | |
13 | PKG_CHECK_MODULES([MONO_ADDINS_GUI], [mono-addins-gui]) | |
14 | PKG_CHECK_MODULES([MONO_ADDINS_SETUP], [mono-addins-setup]) | |
15 | PKG_CHECK_MODULES([MONO_CAIRO], [mono-cairo]) | |
16 | -PKG_CHECK_MODULES([MONO_OPTIONS], [mono-options]) | |
17 | PKG_CHECK_MODULES([NDESK_DBUS_10], [ndesk-dbus-1.0]) | |
18 | PKG_CHECK_MODULES([NDESK_DBUS_GLIB_10], [ndesk-dbus-glib-1.0]) | |
19 | PKG_CHECK_MODULES([NOTIFY_SHARP], [notify-sharp]) | |
20 | @@ -120,7 +118,6 @@ | |
21 | MONO_CAIRO_LIBS="-r:Mono.Cairo" | |
22 | AC_SUBST([MONO_CAIRO_LIBS]) | |
23 | ||
24 | -AC_SUBST([MONO_OPTIONS_LIBS]) | |
25 | AC_SUBST([NDESK_DBUS_10_LIBS]) | |
26 | AC_SUBST([NDESK_DBUS_GLIB_10_LIBS]) | |
27 | AC_SUBST([NOTIFY_SHARP_LIBS]) | |
28 | diff -u -Nr docky-2.0.7/Docky/AssemblyInfo.cs docky-2.0.7.build/Docky/AssemblyInfo.cs | |
29 | --- docky-2.0.7/Docky/AssemblyInfo.cs 2010-10-02 04:01:38.090463001 -0700 | |
30 | +++ docky-2.0.7.build/Docky/AssemblyInfo.cs 2010-10-19 01:55:41.430017686 -0700 | |
31 | @@ -17,7 +17,6 @@ | |
32 | ||
33 | using System.Reflection; | |
34 | using System.Runtime.CompilerServices; | |
35 | -using Mono.GetOptions; | |
36 | ||
37 | // Information about this assembly is defined by the following | |
38 | // attributes. | |
39 | diff -u -Nr docky-2.0.7/Docky/AssemblyInfo.cs.in docky-2.0.7.build/Docky/AssemblyInfo.cs.in | |
40 | --- docky-2.0.7/Docky/AssemblyInfo.cs.in 2010-06-26 06:47:36.543229000 -0700 | |
41 | +++ docky-2.0.7.build/Docky/AssemblyInfo.cs.in 2010-10-19 01:54:37.573351289 -0700 | |
42 | @@ -17,7 +17,6 @@ | |
43 | ||
44 | using System.Reflection; | |
45 | using System.Runtime.CompilerServices; | |
46 | -using Mono.GetOptions; | |
47 | ||
48 | // Information about this assembly is defined by the following | |
49 | // attributes. | |
50 | @@ -33,13 +32,6 @@ | |
51 | [assembly: AssemblyTrademark("")] | |
52 | [assembly: AssemblyCulture("")] | |
53 | ||
54 | -[assembly: Mono.About ("For more information, please visit http://launchpad.net/docky/")] | |
55 | -[assembly: Mono.Author ("Daniel For\u0233 <daniel.p.fore@gmail.com>")] | |
56 | -[assembly: Mono.Author ("Chris Szikszoy <chris@szikszoy.com>")] | |
57 | -[assembly: Mono.Author ("Robert Dyer <psybers@gmail.com>")] | |
58 | -[assembly: Mono.Author ("Jason Smith <jassmith@gmail.com>")] | |
59 | -[assembly: Mono.UsageComplement ("")] | |
60 | - | |
61 | // The assembly version has following format: | |
62 | // | |
63 | // Major.Minor.Build.Revision | |
64 | diff -u -Nr docky-2.0.7/Docky/Docky/Docky.cs docky-2.0.7.build/Docky/Docky/Docky.cs | |
65 | --- docky-2.0.7/Docky/Docky/Docky.cs 2010-08-24 11:41:52.206278994 -0700 | |
66 | +++ docky-2.0.7.build/Docky/Docky/Docky.cs 2010-10-19 02:05:26.020019496 -0700 | |
67 | @@ -37,7 +37,7 @@ | |
68 | { | |
69 | public static class Docky | |
70 | { | |
71 | - public static UserArgs CommandLinePreferences { get; private set; } | |
72 | + public static UserArgs UserArgs { get; private set; } | |
73 | ||
74 | static DockController controller; | |
75 | internal static DockController Controller { | |
76 | @@ -66,7 +66,8 @@ | |
77 | GLib.GType.Init (); | |
78 | ||
79 | // process the command line args | |
80 | - CommandLinePreferences = new UserArgs (args); | |
81 | + if (!UserArgs.Parse (args)) | |
82 | + return; | |
83 | ||
84 | Wnck.Global.ClientType = Wnck.ClientType.Pager; | |
85 | ||
86 | diff -u -Nr docky-2.0.7/Docky/Docky/Interface/CursorTracker.cs docky-2.0.7.build/Docky/Docky/Interface/CursorTracker.cs | |
87 | --- docky-2.0.7/Docky/Docky/Interface/CursorTracker.cs 2010-06-26 06:47:36.543229000 -0700 | |
88 | +++ docky-2.0.7.build/Docky/Docky/Interface/CursorTracker.cs 2010-10-19 01:54:40.130014783 -0700 | |
89 | @@ -113,7 +113,7 @@ | |
90 | if (timer_speed != length) { | |
91 | if (timer > 0) | |
92 | GLib.Source.Remove (timer); | |
93 | - if (!Docky.CommandLinePreferences.NoPollCursor) | |
94 | + if (!UserArgs.NoPollCursor) | |
95 | timer = GLib.Timeout.Add (length, OnTimerTick); | |
96 | timer_speed = length; | |
97 | } | |
98 | diff -u -Nr docky-2.0.7/Docky/Docky/Interface/DockWindow.cs docky-2.0.7.build/Docky/Docky/Interface/DockWindow.cs | |
99 | --- docky-2.0.7/Docky/Docky/Interface/DockWindow.cs 2010-10-01 00:00:03.733783001 -0700 | |
100 | +++ docky-2.0.7.build/Docky/Docky/Interface/DockWindow.cs 2010-10-19 02:07:49.340017589 -0700 | |
101 | @@ -730,8 +730,8 @@ | |
102 | Wnck.Screen.Default.WindowOpened += HandleWindowOpened; | |
103 | ||
104 | // fix for nvidia bug | |
105 | - if (Docky.CommandLinePreferences.BufferTime > 0) | |
106 | - GLib.Timeout.Add (Docky.CommandLinePreferences.BufferTime * 60 * 1000, delegate { | |
107 | + if (UserArgs.BufferTime > 0) | |
108 | + GLib.Timeout.Add (UserArgs.BufferTime * 60 * 1000, delegate { | |
109 | ResetBuffers (); | |
110 | return true; | |
111 | }); | |
112 | @@ -1498,14 +1498,14 @@ | |
113 | UpdateMonitorGeometry (); | |
114 | ||
115 | if (VerticalDock) { | |
116 | - Height = Math.Min (Docky.CommandLinePreferences.MaxSize, monitor_geo.Height); | |
117 | + Height = Math.Min (UserArgs.MaxSize, monitor_geo.Height); | |
118 | Width = DockHeightBuffer + ZoomedIconSize + UrgentBounceHeight; | |
119 | } else { | |
120 | - Width = Math.Min (Docky.CommandLinePreferences.MaxSize, monitor_geo.Width); | |
121 | + Width = Math.Min (UserArgs.MaxSize, monitor_geo.Width); | |
122 | Height = DockHeightBuffer + ZoomedIconSize + UrgentBounceHeight; | |
123 | } | |
124 | ||
125 | - if (Docky.CommandLinePreferences.NetbookMode) { | |
126 | + if (UserArgs.NetbookMode) { | |
127 | // Currently the intel i945 series of cards (used on netbooks frequently) will | |
128 | // for some mystical reason get terrible drawing performance if the window is | |
129 | // between 1009 pixels and 1024 pixels in width OR height. We just pad it out an extra | |
130 | diff -u -Nr docky-2.0.7/Docky/Docky/UserArgs.cs docky-2.0.7.build/Docky/Docky/UserArgs.cs | |
131 | --- docky-2.0.7/Docky/Docky/UserArgs.cs 2010-06-26 06:47:36.543229000 -0700 | |
132 | +++ docky-2.0.7.build/Docky/Docky/UserArgs.cs 2010-10-19 01:54:40.136682364 -0700 | |
133 | @@ -19,57 +19,106 @@ | |
134 | ||
135 | using Docky.Services; | |
136 | ||
137 | -using Mono.GetOptions; | |
138 | - | |
139 | -// disable the warning message about Mono.GetOptions.Options being obsolete | |
140 | -#pragma warning disable 618 | |
141 | +using Mono.Options; | |
142 | ||
143 | namespace Docky | |
144 | { | |
145 | - public class UserArgs : Options | |
146 | + public class UserArgs | |
147 | { | |
148 | - [Option ("Disable cursor polling (for testing)", 'p', "disable-polling")] | |
149 | - public bool NoPollCursor; | |
150 | - | |
151 | - [Option ("Maximum window dimension (min 500)", 'm', "max-size")] | |
152 | - public int MaxSize; | |
153 | - | |
154 | - [Option ("Enable debug level logging", 'd', "debug")] | |
155 | - public bool Debug; | |
156 | + const int FORCE_BUFFER_REFRESH = 10; | |
157 | ||
158 | - [Option ("Netbook mode", 'n', "netbook")] | |
159 | - public bool NetbookMode; | |
160 | + /// <summary> | |
161 | + /// Gets or sets a value indicating whether cursor polling should be disabled. | |
162 | + /// </summary> | |
163 | + /// <value> | |
164 | + /// <c>true</c> cursor polling should be disabled; otherwise, <c>false</c>. | |
165 | + /// </value> | |
166 | + public static bool NoPollCursor { get; private set; } | |
167 | + /// <summary> | |
168 | + /// Gets or sets the maximum dock window size. | |
169 | + /// </summary> | |
170 | + /// <value> | |
171 | + /// The max size of the dock window. | |
172 | + /// </value> | |
173 | + public static int MaxSize { get; private set; } | |
174 | + /// <summary> | |
175 | + /// Gets or sets a value indicating whether Docky should run in netbook mode. | |
176 | + /// </summary> | |
177 | + /// <value> | |
178 | + /// <c>true</c> if netbook mode; otherwise, <c>false</c>. | |
179 | + /// </value> | |
180 | + public static bool NetbookMode { get; private set; } | |
181 | + /// <summary> | |
182 | + /// The number of minutes to keep buffers. | |
183 | + /// </summary> | |
184 | + /// <value> | |
185 | + /// The buffer refresh interval. | |
186 | + /// </value> | |
187 | + public static uint BufferTime { get; private set; } | |
188 | ||
189 | - [Option ("Nvidia mode (for Nvidia cards that lag after awhile) [-b 10]", "nvidia")] | |
190 | - public bool NvidiaMode; | |
191 | + static bool HelpShown { get; set; } | |
192 | + static OptionSet Options { get; set; } | |
193 | ||
194 | - [Option ("Maximum time (in minutes) to keep buffers", 'b', "buffer-time")] | |
195 | - public uint BufferTime; | |
196 | - | |
197 | - public UserArgs (string[] args) | |
198 | + static UserArgs () | |
199 | { | |
200 | - Log.DisplayLevel = LogLevel.Warn; | |
201 | + MaxSize = int.MaxValue; | |
202 | ||
203 | - ParsingMode = OptionsParsingMode.GNU_DoubleDash; | |
204 | - ProcessArgs (args); | |
205 | + Options = new OptionSet () { | |
206 | + { "p|disable-polling", "Disable cursor polling (for testing)", val => NoPollCursor = true }, | |
207 | + { "m|max-size=", "Maximum window dimension (min 500)", (int max) => MaxSize = Math.Max (max, 500) }, | |
208 | + { "d|debug", "Enable debug logging", debug => { | |
209 | + Log.DisplayLevel = (debug == null) ? LogLevel.Warn : LogLevel.Debug; | |
210 | + } }, | |
211 | + { "n|netbook", "Netbook mode", netbook => NetbookMode = true }, | |
212 | + { "nv|nvidia", "Nvidia mode (for Nvidia cards that lag after a while). Equivalent to '-b 10'.", | |
213 | + nv => { | |
214 | + if (BufferTime == 0) | |
215 | + BufferTime = FORCE_BUFFER_REFRESH; | |
216 | + } }, | |
217 | + { "b|buffer-time=", "Maximum time (in minutes) to keep buffers", (uint buf) => BufferTime = buf }, | |
218 | + { "h|?|help", "Show this help list", help => ShowHelp () }, | |
219 | + }; | |
220 | + } | |
221 | + | |
222 | + /// <summary> | |
223 | + /// Parse the specified command line args. | |
224 | + /// </summary> | |
225 | + /// <param name='args'> | |
226 | + /// The arguments to parse. | |
227 | + /// </param> | |
228 | + /// <returns> | |
229 | + /// <c>true</c> if help was shown, otherwise <c>false</c>. | |
230 | + /// </returns> | |
231 | + public static bool Parse (string[] args) | |
232 | + { | |
233 | + try { | |
234 | + Options.Parse (args); | |
235 | + } catch (OptionException ex) { | |
236 | + Log<UserArgs>.Error ("Error parsing options: {0}", ex.Message); | |
237 | + ShowHelp (); | |
238 | + } | |
239 | ||
240 | - // defaults | |
241 | - NvidiaMode |= DockServices.System.HasNvidia; | |
242 | - if (MaxSize == 0) | |
243 | - MaxSize = int.MaxValue; | |
244 | - MaxSize = Math.Max (MaxSize, 500); | |
245 | - if (NvidiaMode && BufferTime == 0) | |
246 | - BufferTime = 10; | |
247 | - // if the debug option was passed, set it to debug | |
248 | - // otherwise leave it to the default, which is warn | |
249 | - if (Debug) | |
250 | - Log.DisplayLevel = LogLevel.Debug; | |
251 | + // if the buffer time wasn't explicity set, and a Nvidia card is present, | |
252 | + // force the buffer refresh time to 10 minutes | |
253 | + if (DockServices.System.HasNvidia && BufferTime == 0) | |
254 | + BufferTime = FORCE_BUFFER_REFRESH; | |
255 | ||
256 | // log the parsed user args | |
257 | Log<UserArgs>.Debug ("BufferTime = " + BufferTime); | |
258 | Log<UserArgs>.Debug ("MaxSize = " + MaxSize); | |
259 | Log<UserArgs>.Debug ("NetbookMode = " + NetbookMode); | |
260 | Log<UserArgs>.Debug ("NoPollCursor = " + NoPollCursor); | |
261 | + | |
262 | + // if the help was shown, return false, alerting the main thread to exit | |
263 | + return !HelpShown; | |
264 | + } | |
265 | + | |
266 | + static void ShowHelp () | |
267 | + { | |
268 | + Console.WriteLine ("usage: docky [options]"); | |
269 | + Console.WriteLine (); | |
270 | + Options.WriteOptionDescriptions (Console.Out); | |
271 | + HelpShown = true; | |
272 | } | |
273 | } | |
274 | } | |
275 | diff -u -Nr docky-2.0.7/Docky/Makefile.am docky-2.0.7.build/Docky/Makefile.am | |
276 | --- docky-2.0.7/Docky/Makefile.am 2010-09-15 23:54:05.277920997 -0700 | |
277 | +++ docky-2.0.7.build/Docky/Makefile.am 2010-10-19 01:55:27.843351708 -0700 | |
278 | @@ -10,6 +10,7 @@ | |
279 | gtk-gui/Docky.Interface.DockPreferences.cs \ | |
280 | gtk-gui/Docky.ConfigurationWindow.cs \ | |
281 | Gtk/Widget_Extensions.cs \ | |
282 | + Mono/Options.cs \ | |
283 | Docky/ConfigurationWindow.cs \ | |
284 | Docky/DockController.cs \ | |
285 | Docky/Docky.cs \ | |
286 | @@ -65,7 +66,6 @@ | |
287 | System \ | |
288 | System.Core \ | |
289 | Mono.Posix \ | |
290 | - Mono.GetOptions \ | |
291 | $(MONO_CAIRO_LIBS) \ | |
292 | $(GIO_SHARP_LIBS) \ | |
293 | $(GLIB_SHARP_20_LIBS) \ | |
294 | @@ -90,4 +90,4 @@ | |
295 | module_DATA += Docky.addins | |
296 | bin_SCRIPTS = docky | |
297 | ||
298 | -MCS_FLAGS += -unsafe | |
299 | +MCS_FLAGS += -d:LINQ -unsafe | |
300 | diff -u -Nr docky-2.0.7/Docky/Makefile.in docky-2.0.7.build/Docky/Makefile.in | |
301 | --- docky-2.0.7/Docky/Makefile.in 2010-10-02 04:01:31.670463001 -0700 | |
302 | +++ docky-2.0.7.build/Docky/Makefile.in 2010-10-19 01:55:15.670019205 -0700 | |
303 | @@ -382,7 +382,6 @@ | |
304 | System \ | |
305 | System.Core \ | |
306 | Mono.Posix \ | |
307 | - Mono.GetOptions \ | |
308 | $(MONO_CAIRO_LIBS) \ | |
309 | $(GIO_SHARP_LIBS) \ | |
310 | $(GLIB_SHARP_20_LIBS) \ | |
311 | diff -u -Nr docky-2.0.7/Docky/Mono/Options.cs docky-2.0.7.build/Docky/Mono/Options.cs | |
312 | --- docky-2.0.7/Docky/Mono/Options.cs 1969-12-31 16:00:00.000000000 -0800 | |
313 | +++ docky-2.0.7.build/Docky/Mono/Options.cs 2010-10-19 01:54:40.136682364 -0700 | |
314 | @@ -0,0 +1,1101 @@ | |
315 | +// | |
316 | +// Options.cs | |
317 | +// | |
318 | +// Authors: | |
319 | +// Jonathan Pryor <jpryor@novell.com> | |
320 | +// | |
321 | +// Copyright (C) 2008 Novell (http://www.novell.com) | |
322 | +// | |
323 | +// Permission is hereby granted, free of charge, to any person obtaining | |
324 | +// a copy of this software and associated documentation files (the | |
325 | +// "Software"), to deal in the Software without restriction, including | |
326 | +// without limitation the rights to use, copy, modify, merge, publish, | |
327 | +// distribute, sublicense, and/or sell copies of the Software, and to | |
328 | +// permit persons to whom the Software is furnished to do so, subject to | |
329 | +// the following conditions: | |
330 | +// | |
331 | +// The above copyright notice and this permission notice shall be | |
332 | +// included in all copies or substantial portions of the Software. | |
333 | +// | |
334 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
335 | +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
336 | +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
337 | +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
338 | +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
339 | +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
340 | +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
341 | +// | |
342 | + | |
343 | +// Compile With: | |
344 | +// gmcs -debug+ -r:System.Core Options.cs -o:NDesk.Options.dll | |
345 | +// gmcs -debug+ -d:LINQ -r:System.Core Options.cs -o:NDesk.Options.dll | |
346 | +// | |
347 | +// The LINQ version just changes the implementation of | |
348 | +// OptionSet.Parse(IEnumerable<string>), and confers no semantic changes. | |
349 | + | |
350 | +// | |
351 | +// A Getopt::Long-inspired option parsing library for C#. | |
352 | +// | |
353 | +// NDesk.Options.OptionSet is built upon a key/value table, where the | |
354 | +// key is a option format string and the value is a delegate that is | |
355 | +// invoked when the format string is matched. | |
356 | +// | |
357 | +// Option format strings: | |
358 | +// Regex-like BNF Grammar: | |
359 | +// name: .+ | |
360 | +// type: [=:] | |
361 | +// sep: ( [^{}]+ | '{' .+ '}' )? | |
362 | +// aliases: ( name type sep ) ( '|' name type sep )* | |
363 | +// | |
364 | +// Each '|'-delimited name is an alias for the associated action. If the | |
365 | +// format string ends in a '=', it has a required value. If the format | |
366 | +// string ends in a ':', it has an optional value. If neither '=' or ':' | |
367 | +// is present, no value is supported. `=' or `:' need only be defined on one | |
368 | +// alias, but if they are provided on more than one they must be consistent. | |
369 | +// | |
370 | +// Each alias portion may also end with a "key/value separator", which is used | |
371 | +// to split option values if the option accepts > 1 value. If not specified, | |
372 | +// it defaults to '=' and ':'. If specified, it can be any character except | |
373 | +// '{' and '}' OR the *string* between '{' and '}'. If no separator should be | |
374 | +// used (i.e. the separate values should be distinct arguments), then "{}" | |
375 | +// should be used as the separator. | |
376 | +// | |
377 | +// Options are extracted either from the current option by looking for | |
378 | +// the option name followed by an '=' or ':', or is taken from the | |
379 | +// following option IFF: | |
380 | +// - The current option does not contain a '=' or a ':' | |
381 | +// - The current option requires a value (i.e. not a Option type of ':') | |
382 | +// | |
383 | +// The `name' used in the option format string does NOT include any leading | |
384 | +// option indicator, such as '-', '--', or '/'. All three of these are | |
385 | +// permitted/required on any named option. | |
386 | +// | |
387 | +// Option bundling is permitted so long as: | |
388 | +// - '-' is used to start the option group | |
389 | +// - all of the bundled options are a single character | |
390 | +// - at most one of the bundled options accepts a value, and the value | |
391 | +// provided starts from the next character to the end of the string. | |
392 | +// | |
393 | +// This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value' | |
394 | +// as '-Dname=value'. | |
395 | +// | |
396 | +// Option processing is disabled by specifying "--". All options after "--" | |
397 | +// are returned by OptionSet.Parse() unchanged and unprocessed. | |
398 | +// | |
399 | +// Unprocessed options are returned from OptionSet.Parse(). | |
400 | +// | |
401 | +// Examples: | |
402 | +// int verbose = 0; | |
403 | +// OptionSet p = new OptionSet () | |
404 | +// .Add ("v", v => ++verbose) | |
405 | +// .Add ("name=|value=", v => Console.WriteLine (v)); | |
406 | +// p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}); | |
407 | +// | |
408 | +// The above would parse the argument string array, and would invoke the | |
409 | +// lambda expression three times, setting `verbose' to 3 when complete. | |
410 | +// It would also print out "A" and "B" to standard output. | |
411 | +// The returned array would contain the string "extra". | |
412 | +// | |
413 | +// C# 3.0 collection initializers are supported and encouraged: | |
414 | +// var p = new OptionSet () { | |
415 | +// { "h|?|help", v => ShowHelp () }, | |
416 | +// }; | |
417 | +// | |
418 | +// System.ComponentModel.TypeConverter is also supported, allowing the use of | |
419 | +// custom data types in the callback type; TypeConverter.ConvertFromString() | |
420 | +// is used to convert the value option to an instance of the specified | |
421 | +// type: | |
422 | +// | |
423 | +// var p = new OptionSet () { | |
424 | +// { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, | |
425 | +// }; | |
426 | +// | |
427 | +// Random other tidbits: | |
428 | +// - Boolean options (those w/o '=' or ':' in the option format string) | |
429 | +// are explicitly enabled if they are followed with '+', and explicitly | |
430 | +// disabled if they are followed with '-': | |
431 | +// string a = null; | |
432 | +// var p = new OptionSet () { | |
433 | +// { "a", s => a = s }, | |
434 | +// }; | |
435 | +// p.Parse (new string[]{"-a"}); // sets v != null | |
436 | +// p.Parse (new string[]{"-a+"}); // sets v != null | |
437 | +// p.Parse (new string[]{"-a-"}); // sets v == null | |
438 | +// | |
439 | + | |
440 | +using System; | |
441 | +using System.Collections; | |
442 | +using System.Collections.Generic; | |
443 | +using System.Collections.ObjectModel; | |
444 | +using System.ComponentModel; | |
445 | +using System.Globalization; | |
446 | +using System.IO; | |
447 | +using System.Runtime.Serialization; | |
448 | +using System.Security.Permissions; | |
449 | +using System.Text; | |
450 | +using System.Text.RegularExpressions; | |
451 | + | |
452 | +#if LINQ | |
453 | +using System.Linq; | |
454 | +#endif | |
455 | + | |
456 | +#if TEST | |
457 | +using NDesk.Options; | |
458 | +#endif | |
459 | + | |
460 | +#if NDESK_OPTIONS | |
461 | +namespace NDesk.Options | |
462 | +#else | |
463 | +namespace Mono.Options | |
464 | +#endif | |
465 | +{ | |
466 | + public class OptionValueCollection : IList, IList<string> { | |
467 | + | |
468 | + List<string> values = new List<string> (); | |
469 | + OptionContext c; | |
470 | + | |
471 | + internal OptionValueCollection (OptionContext c) | |
472 | + { | |
473 | + this.c = c; | |
474 | + } | |
475 | + | |
476 | + #region ICollection | |
477 | + void ICollection.CopyTo (Array array, int index) {(values as ICollection).CopyTo (array, index);} | |
478 | + bool ICollection.IsSynchronized {get {return (values as ICollection).IsSynchronized;}} | |
479 | + object ICollection.SyncRoot {get {return (values as ICollection).SyncRoot;}} | |
480 | + #endregion | |
481 | + | |
482 | + #region ICollection<T> | |
483 | + public void Add (string item) {values.Add (item);} | |
484 | + public void Clear () {values.Clear ();} | |
485 | + public bool Contains (string item) {return values.Contains (item);} | |
486 | + public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);} | |
487 | + public bool Remove (string item) {return values.Remove (item);} | |
488 | + public int Count {get {return values.Count;}} | |
489 | + public bool IsReadOnly {get {return false;}} | |
490 | + #endregion | |
491 | + | |
492 | + #region IEnumerable | |
493 | + IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();} | |
494 | + #endregion | |
495 | + | |
496 | + #region IEnumerable<T> | |
497 | + public IEnumerator<string> GetEnumerator () {return values.GetEnumerator ();} | |
498 | + #endregion | |
499 | + | |
500 | + #region IList | |
501 | + int IList.Add (object value) {return (values as IList).Add (value);} | |
502 | + bool IList.Contains (object value) {return (values as IList).Contains (value);} | |
503 | + int IList.IndexOf (object value) {return (values as IList).IndexOf (value);} | |
504 | + void IList.Insert (int index, object value) {(values as IList).Insert (index, value);} | |
505 | + void IList.Remove (object value) {(values as IList).Remove (value);} | |
506 | + void IList.RemoveAt (int index) {(values as IList).RemoveAt (index);} | |
507 | + bool IList.IsFixedSize {get {return false;}} | |
508 | + object IList.this [int index] {get {return this [index];} set {(values as IList)[index] = value;}} | |
509 | + #endregion | |
510 | + | |
511 | + #region IList<T> | |
512 | + public int IndexOf (string item) {return values.IndexOf (item);} | |
513 | + public void Insert (int index, string item) {values.Insert (index, item);} | |
514 | + public void RemoveAt (int index) {values.RemoveAt (index);} | |
515 | + | |
516 | + private void AssertValid (int index) | |
517 | + { | |
518 | + if (c.Option == null) | |
519 | + throw new InvalidOperationException ("OptionContext.Option is null."); | |
520 | + if (index >= c.Option.MaxValueCount) | |
521 | + throw new ArgumentOutOfRangeException ("index"); | |
522 | + if (c.Option.OptionValueType == OptionValueType.Required && | |
523 | + index >= values.Count) | |
524 | + throw new OptionException (string.Format ( | |
525 | + c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), | |
526 | + c.OptionName); | |
527 | + } | |
528 | + | |
529 | + public string this [int index] { | |
530 | + get { | |
531 | + AssertValid (index); | |
532 | + return index >= values.Count ? null : values [index]; | |
533 | + } | |
534 | + set { | |
535 | + values [index] = value; | |
536 | + } | |
537 | + } | |
538 | + #endregion | |
539 | + | |
540 | + public List<string> ToList () | |
541 | + { | |
542 | + return new List<string> (values); | |
543 | + } | |
544 | + | |
545 | + public string[] ToArray () | |
546 | + { | |
547 | + return values.ToArray (); | |
548 | + } | |
549 | + | |
550 | + public override string ToString () | |
551 | + { | |
552 | + return string.Join (", ", values.ToArray ()); | |
553 | + } | |
554 | + } | |
555 | + | |
556 | + public class OptionContext { | |
557 | + private Option option; | |
558 | + private string name; | |
559 | + private int index; | |
560 | + private OptionSet set; | |
561 | + private OptionValueCollection c; | |
562 | + | |
563 | + public OptionContext (OptionSet set) | |
564 | + { | |
565 | + this.set = set; | |
566 | + this.c = new OptionValueCollection (this); | |
567 | + } | |
568 | + | |
569 | + public Option Option { | |
570 | + get {return option;} | |
571 | + set {option = value;} | |
572 | + } | |
573 | + | |
574 | + public string OptionName { | |
575 | + get {return name;} | |
576 | + set {name = value;} | |
577 | + } | |
578 | + | |
579 | + public int OptionIndex { | |
580 | + get {return index;} | |
581 | + set {index = value;} | |
582 | + } | |
583 | + | |
584 | + public OptionSet OptionSet { | |
585 | + get {return set;} | |
586 | + } | |
587 | + | |
588 | + public OptionValueCollection OptionValues { | |
589 | + get {return c;} | |
590 | + } | |
591 | + } | |
592 | + | |
593 | + public enum OptionValueType { | |
594 | + None, | |
595 | + Optional, | |
596 | + Required, | |
597 | + } | |
598 | + | |
599 | + public abstract class Option { | |
600 | + string prototype, description; | |
601 | + string[] names; | |
602 | + OptionValueType type; | |
603 | + int count; | |
604 | + string[] separators; | |
605 | + | |
606 | + protected Option (string prototype, string description) | |
607 | + : this (prototype, description, 1) | |
608 | + { | |
609 | + } | |
610 | + | |
611 | + protected Option (string prototype, string description, int maxValueCount) | |
612 | + { | |
613 | + if (prototype == null) | |
614 | + throw new ArgumentNullException ("prototype"); | |
615 | + if (prototype.Length == 0) | |
616 | + throw new ArgumentException ("Cannot be the empty string.", "prototype"); | |
617 | + if (maxValueCount < 0) | |
618 | + throw new ArgumentOutOfRangeException ("maxValueCount"); | |
619 | + | |
620 | + this.prototype = prototype; | |
621 | + this.names = prototype.Split ('|'); | |
622 | + this.description = description; | |
623 | + this.count = maxValueCount; | |
624 | + this.type = ParsePrototype (); | |
625 | + | |
626 | + if (this.count == 0 && type != OptionValueType.None) | |
627 | + throw new ArgumentException ( | |
628 | + "Cannot provide maxValueCount of 0 for OptionValueType.Required or " + | |
629 | + "OptionValueType.Optional.", | |
630 | + "maxValueCount"); | |
631 | + if (this.type == OptionValueType.None && maxValueCount > 1) | |
632 | + throw new ArgumentException ( | |
633 | + string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount), | |
634 | + "maxValueCount"); | |
635 | + if (Array.IndexOf (names, "<>") >= 0 && | |
636 | + ((names.Length == 1 && this.type != OptionValueType.None) || | |
637 | + (names.Length > 1 && this.MaxValueCount > 1))) | |
638 | + throw new ArgumentException ( | |
639 | + "The default option handler '<>' cannot require values.", | |
640 | + "prototype"); | |
641 | + } | |
642 | + | |
643 | + public string Prototype {get {return prototype;}} | |
644 | + public string Description {get {return description;}} | |
645 | + public OptionValueType OptionValueType {get {return type;}} | |
646 | + public int MaxValueCount {get {return count;}} | |
647 | + | |
648 | + public string[] GetNames () | |
649 | + { | |
650 | + return (string[]) names.Clone (); | |
651 | + } | |
652 | + | |
653 | + public string[] GetValueSeparators () | |
654 | + { | |
655 | + if (separators == null) | |
656 | + return new string [0]; | |
657 | + return (string[]) separators.Clone (); | |
658 | + } | |
659 | + | |
660 | + protected static T Parse<T> (string value, OptionContext c) | |
661 | + { | |
662 | + Type tt = typeof (T); | |
663 | + bool nullable = tt.IsValueType && tt.IsGenericType && | |
664 | + !tt.IsGenericTypeDefinition && | |
665 | + tt.GetGenericTypeDefinition () == typeof (Nullable<>); | |
666 | + Type targetType = nullable ? tt.GetGenericArguments () [0] : typeof (T); | |
667 | + TypeConverter conv = TypeDescriptor.GetConverter (targetType); | |
668 | + T t = default (T); | |
669 | + try { | |
670 | + if (value != null) | |
671 | + t = (T) conv.ConvertFromString (value); | |
672 | + } | |
673 | + catch (Exception e) { | |
674 | + throw new OptionException ( | |
675 | + string.Format ( | |
676 | + c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."), | |
677 | + value, targetType.Name, c.OptionName), | |
678 | + c.OptionName, e); | |
679 | + } | |
680 | + return t; | |
681 | + } | |
682 | + | |
683 | + internal string[] Names {get {return names;}} | |
684 | + internal string[] ValueSeparators {get {return separators;}} | |
685 | + | |
686 | + static readonly char[] NameTerminator = new char[]{'=', ':'}; | |
687 | + | |
688 | + private OptionValueType ParsePrototype () | |
689 | + { | |
690 | + char type = '\0'; | |
691 | + List<string> seps = new List<string> (); | |
692 | + for (int i = 0; i < names.Length; ++i) { | |
693 | + string name = names [i]; | |
694 | + if (name.Length == 0) | |
695 | + throw new ArgumentException ("Empty option names are not supported.", "prototype"); | |
696 | + | |
697 | + int end = name.IndexOfAny (NameTerminator); | |
698 | + if (end == -1) | |
699 | + continue; | |
700 | + names [i] = name.Substring (0, end); | |
701 | + if (type == '\0' || type == name [end]) | |
702 | + type = name [end]; | |
703 | + else | |
704 | + throw new ArgumentException ( | |
705 | + string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]), | |
706 | + "prototype"); | |
707 | + AddSeparators (name, end, seps); | |
708 | + } | |
709 | + | |
710 | + if (type == '\0') | |
711 | + return OptionValueType.None; | |
712 | + | |
713 | + if (count <= 1 && seps.Count != 0) | |
714 | + throw new ArgumentException ( | |
715 | + string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count), | |
716 | + "prototype"); | |
717 | + if (count > 1) { | |
718 | + if (seps.Count == 0) | |
719 | + this.separators = new string[]{":", "="}; | |
720 | + else if (seps.Count == 1 && seps [0].Length == 0) | |
721 | + this.separators = null; | |
722 | + else | |
723 | + this.separators = seps.ToArray (); | |
724 | + } | |
725 | + | |
726 | + return type == '=' ? OptionValueType.Required : OptionValueType.Optional; | |
727 | + } | |
728 | + | |
729 | + private static void AddSeparators (string name, int end, ICollection<string> seps) | |
730 | + { | |
731 | + int start = -1; | |
732 | + for (int i = end+1; i < name.Length; ++i) { | |
733 | + switch (name [i]) { | |
734 | + case '{': | |
735 | + if (start != -1) | |
736 | + throw new ArgumentException ( | |
737 | + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), | |
738 | + "prototype"); | |
739 | + start = i+1; | |
740 | + break; | |
741 | + case '}': | |
742 | + if (start == -1) | |
743 | + throw new ArgumentException ( | |
744 | + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), | |
745 | + "prototype"); | |
746 | + seps.Add (name.Substring (start, i-start)); | |
747 | + start = -1; | |
748 | + break; | |
749 | + default: | |
750 | + if (start == -1) | |
751 | + seps.Add (name [i].ToString ()); | |
752 | + break; | |
753 | + } | |
754 | + } | |
755 | + if (start != -1) | |
756 | + throw new ArgumentException ( | |
757 | + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), | |
758 | + "prototype"); | |
759 | + } | |
760 | + | |
761 | + public void Invoke (OptionContext c) | |
762 | + { | |
763 | + OnParseComplete (c); | |
764 | + c.OptionName = null; | |
765 | + c.Option = null; | |
766 | + c.OptionValues.Clear (); | |
767 | + } | |
768 | + | |
769 | + protected abstract void OnParseComplete (OptionContext c); | |
770 | + | |
771 | + public override string ToString () | |
772 | + { | |
773 | + return Prototype; | |
774 | + } | |
775 | + } | |
776 | + | |
777 | + [Serializable] | |
778 | + public class OptionException : Exception { | |
779 | + private string option; | |
780 | + | |
781 | + public OptionException () | |
782 | + { | |
783 | + } | |
784 | + | |
785 | + public OptionException (string message, string optionName) | |
786 | + : base (message) | |
787 | + { | |
788 | + this.option = optionName; | |
789 | + } | |
790 | + | |
791 | + public OptionException (string message, string optionName, Exception innerException) | |
792 | + : base (message, innerException) | |
793 | + { | |
794 | + this.option = optionName; | |
795 | + } | |
796 | + | |
797 | + protected OptionException (SerializationInfo info, StreamingContext context) | |
798 | + : base (info, context) | |
799 | + { | |
800 | + this.option = info.GetString ("OptionName"); | |
801 | + } | |
802 | + | |
803 | + public string OptionName { | |
804 | + get {return this.option;} | |
805 | + } | |
806 | + | |
807 | + [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] | |
808 | + public override void GetObjectData (SerializationInfo info, StreamingContext context) | |
809 | + { | |
810 | + base.GetObjectData (info, context); | |
811 | + info.AddValue ("OptionName", option); | |
812 | + } | |
813 | + } | |
814 | + | |
815 | + public delegate void OptionAction<TKey, TValue> (TKey key, TValue value); | |
816 | + | |
817 | + public class OptionSet : KeyedCollection<string, Option> | |
818 | + { | |
819 | + public OptionSet () | |
820 | + : this (delegate (string f) {return f;}) | |
821 | + { | |
822 | + } | |
823 | + | |
824 | + public OptionSet (Converter<string, string> localizer) | |
825 | + { | |
826 | + this.localizer = localizer; | |
827 | + } | |
828 | + | |
829 | + Converter<string, string> localizer; | |
830 | + | |
831 | + public Converter<string, string> MessageLocalizer { | |
832 | + get {return localizer;} | |
833 | + } | |
834 | + | |
835 | + protected override string GetKeyForItem (Option item) | |
836 | + { | |
837 | + if (item == null) | |
838 | + throw new ArgumentNullException ("option"); | |
839 | + if (item.Names != null && item.Names.Length > 0) | |
840 | + return item.Names [0]; | |
841 | + // This should never happen, as it's invalid for Option to be | |
842 | + // constructed w/o any names. | |
843 | + throw new InvalidOperationException ("Option has no names!"); | |
844 | + } | |
845 | + | |
846 | + [Obsolete ("Use KeyedCollection.this[string]")] | |
847 | + protected Option GetOptionForName (string option) | |
848 | + { | |
849 | + if (option == null) | |
850 | + throw new ArgumentNullException ("option"); | |
851 | + try { | |
852 | + return base [option]; | |
853 | + } | |
854 | + catch (KeyNotFoundException) { | |
855 | + return null; | |
856 | + } | |
857 | + } | |
858 | + | |
859 | + protected override void InsertItem (int index, Option item) | |
860 | + { | |
861 | + base.InsertItem (index, item); | |
862 | + AddImpl (item); | |
863 | + } | |
864 | + | |
865 | + protected override void RemoveItem (int index) | |
866 | + { | |
867 | + base.RemoveItem (index); | |
868 | + Option p = Items [index]; | |
869 | + // KeyedCollection.RemoveItem() handles the 0th item | |
870 | + for (int i = 1; i < p.Names.Length; ++i) { | |
871 | + Dictionary.Remove (p.Names [i]); | |
872 | + } | |
873 | + } | |
874 | + | |
875 | + protected override void SetItem (int index, Option item) | |
876 | + { | |
877 | + base.SetItem (index, item); | |
878 | + RemoveItem (index); | |
879 | + AddImpl (item); | |
880 | + } | |
881 | + | |
882 | + private void AddImpl (Option option) | |
883 | + { | |
884 | + if (option == null) | |
885 | + throw new ArgumentNullException ("option"); | |
886 | + List<string> added = new List<string> (option.Names.Length); | |
887 | + try { | |
888 | + // KeyedCollection.InsertItem/SetItem handle the 0th name. | |
889 | + for (int i = 1; i < option.Names.Length; ++i) { | |
890 | + Dictionary.Add (option.Names [i], option); | |
891 | + added.Add (option.Names [i]); | |
892 | + } | |
893 | + } | |
894 | + catch (Exception) { | |
895 | + foreach (string name in added) | |
896 | + Dictionary.Remove (name); | |
897 | + throw; | |
898 | + } | |
899 | + } | |
900 | + | |
901 | + public new OptionSet Add (Option option) | |
902 | + { | |
903 | + base.Add (option); | |
904 | + return this; | |
905 | + } | |
906 | + | |
907 | + sealed class ActionOption : Option { | |
908 | + Action<OptionValueCollection> action; | |
909 | + | |
910 | + public ActionOption (string prototype, string description, int count, Action<OptionValueCollection> action) | |
911 | + : base (prototype, description, count) | |
912 | + { | |
913 | + if (action == null) | |
914 | + throw new ArgumentNullException ("action"); | |
915 | + this.action = action; | |
916 | + } | |
917 | + | |
918 | + protected override void OnParseComplete (OptionContext c) | |
919 | + { | |
920 | + action (c.OptionValues); | |
921 | + } | |
922 | + } | |
923 | + | |
924 | + public OptionSet Add (string prototype, Action<string> action) | |
925 | + { | |
926 | + return Add (prototype, null, action); | |
927 | + } | |
928 | + | |
929 | + public OptionSet Add (string prototype, string description, Action<string> action) | |
930 | + { | |
931 | + if (action == null) | |
932 | + throw new ArgumentNullException ("action"); | |
933 | + Option p = new ActionOption (prototype, description, 1, | |
934 | + delegate (OptionValueCollection v) { action (v [0]); }); | |
935 | + base.Add (p); | |
936 | + return this; | |
937 | + } | |
938 | + | |
939 | + public OptionSet Add (string prototype, OptionAction<string, string> action) | |
940 | + { | |
941 | + return Add (prototype, null, action); | |
942 | + } | |
943 | + | |
944 | + public OptionSet Add (string prototype, string description, OptionAction<string, string> action) | |
945 | + { | |
946 | + if (action == null) | |
947 | + throw new ArgumentNullException ("action"); | |
948 | + Option p = new ActionOption (prototype, description, 2, | |
949 | + delegate (OptionValueCollection v) {action (v [0], v [1]);}); | |
950 | + base.Add (p); | |
951 | + return this; | |
952 | + } | |
953 | + | |
954 | + sealed class ActionOption<T> : Option { | |
955 | + Action<T> action; | |
956 | + | |
957 | + public ActionOption (string prototype, string description, Action<T> action) | |
958 | + : base (prototype, description, 1) | |
959 | + { | |
960 | + if (action == null) | |
961 | + throw new ArgumentNullException ("action"); | |
962 | + this.action = action; | |
963 | + } | |
964 | + | |
965 | + protected override void OnParseComplete (OptionContext c) | |
966 | + { | |
967 | + action (Parse<T> (c.OptionValues [0], c)); | |
968 | + } | |
969 | + } | |
970 | + | |
971 | + sealed class ActionOption<TKey, TValue> : Option { | |
972 | + OptionAction<TKey, TValue> action; | |
973 | + | |
974 | + public ActionOption (string prototype, string description, OptionAction<TKey, TValue> action) | |
975 | + : base (prototype, description, 2) | |
976 | + { | |
977 | + if (action == null) | |
978 | + throw new ArgumentNullException ("action"); | |
979 | + this.action = action; | |
980 | + } | |
981 | + | |
982 | + protected override void OnParseComplete (OptionContext c) | |
983 | + { | |
984 | + action ( | |
985 | + Parse<TKey> (c.OptionValues [0], c), | |
986 | + Parse<TValue> (c.OptionValues [1], c)); | |
987 | + } | |
988 | + } | |
989 | + | |
990 | + public OptionSet Add<T> (string prototype, Action<T> action) | |
991 | + { | |
992 | + return Add (prototype, null, action); | |
993 | + } | |
994 | + | |
995 | + public OptionSet Add<T> (string prototype, string description, Action<T> action) | |
996 | + { | |
997 | + return Add (new ActionOption<T> (prototype, description, action)); | |
998 | + } | |
999 | + | |
1000 | + public OptionSet Add<TKey, TValue> (string prototype, OptionAction<TKey, TValue> action) | |
1001 | + { | |
1002 | + return Add (prototype, null, action); | |
1003 | + } | |
1004 | + | |
1005 | + public OptionSet Add<TKey, TValue> (string prototype, string description, OptionAction<TKey, TValue> action) | |
1006 | + { | |
1007 | + return Add (new ActionOption<TKey, TValue> (prototype, description, action)); | |
1008 | + } | |
1009 | + | |
1010 | + protected virtual OptionContext CreateOptionContext () | |
1011 | + { | |
1012 | + return new OptionContext (this); | |
1013 | + } | |
1014 | + | |
1015 | +#if LINQ | |
1016 | + public List<string> Parse (IEnumerable<string> arguments) | |
1017 | + { | |
1018 | + bool process = true; | |
1019 | + OptionContext c = CreateOptionContext (); | |
1020 | + c.OptionIndex = -1; | |
1021 | + var def = GetOptionForName ("<>"); | |
1022 | + var unprocessed = | |
1023 | + from argument in arguments | |
1024 | + where ++c.OptionIndex >= 0 && (process || def != null) | |
1025 | + ? process | |
1026 | + ? argument == "--" | |
1027 | + ? (process = false) | |
1028 | + : !Parse (argument, c) | |
1029 | + ? def != null | |
1030 | + ? Unprocessed (null, def, c, argument) | |
1031 | + : true | |
1032 | + : false | |
1033 | + : def != null | |
1034 | + ? Unprocessed (null, def, c, argument) | |
1035 | + : true | |
1036 | + : true | |
1037 | + select argument; | |
1038 | + List<string> r = unprocessed.ToList (); | |
1039 | + if (c.Option != null) | |
1040 | + c.Option.Invoke (c); | |
1041 | + return r; | |
1042 | + } | |
1043 | +#else | |
1044 | + public List<string> Parse (IEnumerable<string> arguments) | |
1045 | + { | |
1046 | + OptionContext c = CreateOptionContext (); | |
1047 | + c.OptionIndex = -1; | |
1048 | + bool process = true; | |
1049 | + List<string> unprocessed = new List<string> (); | |
1050 | + Option def = Contains ("<>") ? this ["<>"] : null; | |
1051 | + foreach (string argument in arguments) { | |
1052 | + ++c.OptionIndex; | |
1053 | + if (argument == "--") { | |
1054 | + process = false; | |
1055 | + continue; | |
1056 | + } | |
1057 | + if (!process) { | |
1058 | + Unprocessed (unprocessed, def, c, argument); | |
1059 | + continue; | |
1060 | + } | |
1061 | + if (!Parse (argument, c)) | |
1062 | + Unprocessed (unprocessed, def, c, argument); | |
1063 | + } | |
1064 | + if (c.Option != null) | |
1065 | + c.Option.Invoke (c); | |
1066 | + return unprocessed; | |
1067 | + } | |
1068 | +#endif | |
1069 | + | |
1070 | + private static bool Unprocessed (ICollection<string> extra, Option def, OptionContext c, string argument) | |
1071 | + { | |
1072 | + if (def == null) { | |
1073 | + extra.Add (argument); | |
1074 | + return false; | |
1075 | + } | |
1076 | + c.OptionValues.Add (argument); | |
1077 | + c.Option = def; | |
1078 | + c.Option.Invoke (c); | |
1079 | + return false; | |
1080 | + } | |
1081 | + | |
1082 | + private readonly Regex ValueOption = new Regex ( | |
1083 | + @"^(?<flag>--|-|/)(?<name>[^:=]+)((?<sep>[:=])(?<value>.*))?$"); | |
1084 | + | |
1085 | + protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value) | |
1086 | + { | |
1087 | + if (argument == null) | |
1088 | + throw new ArgumentNullException ("argument"); | |
1089 | + | |
1090 | + flag = name = sep = value = null; | |
1091 | + Match m = ValueOption.Match (argument); | |
1092 | + if (!m.Success) { | |
1093 | + return false; | |
1094 | + } | |
1095 | + flag = m.Groups ["flag"].Value; | |
1096 | + name = m.Groups ["name"].Value; | |
1097 | + if (m.Groups ["sep"].Success && m.Groups ["value"].Success) { | |
1098 | + sep = m.Groups ["sep"].Value; | |
1099 | + value = m.Groups ["value"].Value; | |
1100 | + } | |
1101 | + return true; | |
1102 | + } | |
1103 | + | |
1104 | + protected virtual bool Parse (string argument, OptionContext c) | |
1105 | + { | |
1106 | + if (c.Option != null) { | |
1107 | + ParseValue (argument, c); | |
1108 | + return true; | |
1109 | + } | |
1110 | + | |
1111 | + string f, n, s, v; | |
1112 | + if (!GetOptionParts (argument, out f, out n, out s, out v)) | |
1113 | + return false; | |
1114 | + | |
1115 | + Option p; | |
1116 | + if (Contains (n)) { | |
1117 | + p = this [n]; | |
1118 | + c.OptionName = f + n; | |
1119 | + c.Option = p; | |
1120 | + switch (p.OptionValueType) { | |
1121 | + case OptionValueType.None: | |
1122 | + c.OptionValues.Add (n); | |
1123 | + c.Option.Invoke (c); | |
1124 | + break; | |
1125 | + case OptionValueType.Optional: | |
1126 | + case OptionValueType.Required: | |
1127 | + ParseValue (v, c); | |
1128 | + break; | |
1129 | + } | |
1130 | + return true; | |
1131 | + } | |
1132 | + // no match; is it a bool option? | |
1133 | + if (ParseBool (argument, n, c)) | |
1134 | + return true; | |
1135 | + // is it a bundled option? | |
1136 | + if (ParseBundledValue (f, string.Concat (n + s + v), c)) | |
1137 | + return true; | |
1138 | + | |
1139 | + return false; | |
1140 | + } | |
1141 | + | |
1142 | + private void ParseValue (string option, OptionContext c) | |
1143 | + { | |
1144 | + if (option != null) | |
1145 | + foreach (string o in c.Option.ValueSeparators != null | |
1146 | + ? option.Split (c.Option.ValueSeparators, StringSplitOptions.None) | |
1147 | + : new string[]{option}) { | |
1148 | + c.OptionValues.Add (o); | |
1149 | + } | |
1150 | + if (c.OptionValues.Count == c.Option.MaxValueCount || | |
1151 | + c.Option.OptionValueType == OptionValueType.Optional) | |
1152 | + c.Option.Invoke (c); | |
1153 | + else if (c.OptionValues.Count > c.Option.MaxValueCount) { | |
1154 | + throw new OptionException (localizer (string.Format ( | |
1155 | + "Error: Found {0} option values when expecting {1}.", | |
1156 | + c.OptionValues.Count, c.Option.MaxValueCount)), | |
1157 | + c.OptionName); | |
1158 | + } | |
1159 | + } | |
1160 | + | |
1161 | + private bool ParseBool (string option, string n, OptionContext c) | |
1162 | + { | |
1163 | + Option p; | |
1164 | + string rn; | |
1165 | + if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') && | |
1166 | + Contains ((rn = n.Substring (0, n.Length-1)))) { | |
1167 | + p = this [rn]; | |
1168 | + string v = n [n.Length-1] == '+' ? option : null; | |
1169 | + c.OptionName = option; | |
1170 | + c.Option = p; | |
1171 | + c.OptionValues.Add (v); | |
1172 | + p.Invoke (c); | |
1173 | + return true; | |
1174 | + } | |
1175 | + return false; | |
1176 | + } | |
1177 | + | |
1178 | + private bool ParseBundledValue (string f, string n, OptionContext c) | |
1179 | + { | |
1180 | + if (f != "-") | |
1181 | + return false; | |
1182 | + for (int i = 0; i < n.Length; ++i) { | |
1183 | + Option p; | |
1184 | + string opt = f + n [i].ToString (); | |
1185 | + string rn = n [i].ToString (); | |
1186 | + if (!Contains (rn)) { | |
1187 | + if (i == 0) | |
1188 | + return false; | |
1189 | + throw new OptionException (string.Format (localizer ( | |
1190 | + "Cannot bundle unregistered option '{0}'."), opt), opt); | |
1191 | + } | |
1192 | + p = this [rn]; | |
1193 | + switch (p.OptionValueType) { | |
1194 | + case OptionValueType.None: | |
1195 | + Invoke (c, opt, n, p); | |
1196 | + break; | |
1197 | + case OptionValueType.Optional: | |
1198 | + case OptionValueType.Required: { | |
1199 | + string v = n.Substring (i+1); | |
1200 | + c.Option = p; | |
1201 | + c.OptionName = opt; | |
1202 | + ParseValue (v.Length != 0 ? v : null, c); | |
1203 | + return true; | |
1204 | + } | |
1205 | + default: | |
1206 | + throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType); | |
1207 | + } | |
1208 | + } | |
1209 | + return true; | |
1210 | + } | |
1211 | + | |
1212 | + private static void Invoke (OptionContext c, string name, string value, Option option) | |
1213 | + { | |
1214 | + c.OptionName = name; | |
1215 | + c.Option = option; | |
1216 | + c.OptionValues.Add (value); | |
1217 | + option.Invoke (c); | |
1218 | + } | |
1219 | + | |
1220 | + private const int OptionWidth = 29; | |
1221 | + | |
1222 | + public void WriteOptionDescriptions (TextWriter o) | |
1223 | + { | |
1224 | + foreach (Option p in this) { | |
1225 | + int written = 0; | |
1226 | + if (!WriteOptionPrototype (o, p, ref written)) | |
1227 | + continue; | |
1228 | + | |
1229 | + if (written < OptionWidth) | |
1230 | + o.Write (new string (' ', OptionWidth - written)); | |
1231 | + else { | |
1232 | + o.WriteLine (); | |
1233 | + o.Write (new string (' ', OptionWidth)); | |
1234 | + } | |
1235 | + | |
1236 | + bool indent = false; | |
1237 | + string prefix = new string (' ', OptionWidth+2); | |
1238 | + foreach (string line in GetLines (localizer (GetDescription (p.Description)))) { | |
1239 | + if (indent) | |
1240 | + o.Write (prefix); | |
1241 | + o.WriteLine (line); | |
1242 | + indent = true; | |
1243 | + } | |
1244 | + } | |
1245 | + } | |
1246 | + | |
1247 | + bool WriteOptionPrototype (TextWriter o, Option p, ref int written) | |
1248 | + { | |
1249 | + string[] names = p.Names; | |
1250 | + | |
1251 | + int i = GetNextOptionIndex (names, 0); | |
1252 | + if (i == names.Length) | |
1253 | + return false; | |
1254 | + | |
1255 | + if (names [i].Length == 1) { | |
1256 | + Write (o, ref written, " -"); | |
1257 | + Write (o, ref written, names [0]); | |
1258 | + } | |
1259 | + else { | |
1260 | + Write (o, ref written, " --"); | |
1261 | + Write (o, ref written, names [0]); | |
1262 | + } | |
1263 | + | |
1264 | + for ( i = GetNextOptionIndex (names, i+1); | |
1265 | + i < names.Length; i = GetNextOptionIndex (names, i+1)) { | |
1266 | + Write (o, ref written, ", "); | |
1267 | + Write (o, ref written, names [i].Length == 1 ? "-" : "--"); | |
1268 | + Write (o, ref written, names [i]); | |
1269 | + } | |
1270 | + | |
1271 | + if (p.OptionValueType == OptionValueType.Optional || | |
1272 | + p.OptionValueType == OptionValueType.Required) { | |
1273 | + if (p.OptionValueType == OptionValueType.Optional) { | |
1274 | + Write (o, ref written, localizer ("[")); | |
1275 | + } | |
1276 | + Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description))); | |
1277 | + string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 | |
1278 | + ? p.ValueSeparators [0] | |
1279 | + : " "; | |
1280 | + for (int c = 1; c < p.MaxValueCount; ++c) { | |
1281 | + Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description))); | |
1282 | + } | |
1283 | + if (p.OptionValueType == OptionValueType.Optional) { | |
1284 | + Write (o, ref written, localizer ("]")); | |
1285 | + } | |
1286 | + } | |
1287 | + return true; | |
1288 | + } | |
1289 | + | |
1290 | + static int GetNextOptionIndex (string[] names, int i) | |
1291 | + { | |
1292 | + while (i < names.Length && names [i] == "<>") { | |
1293 | + ++i; | |
1294 | + } | |
1295 | + return i; | |
1296 | + } | |
1297 | + | |
1298 | + static void Write (TextWriter o, ref int n, string s) | |
1299 | + { | |
1300 | + n += s.Length; | |
1301 | + o.Write (s); | |
1302 | + } | |
1303 | + | |
1304 | + private static string GetArgumentName (int index, int maxIndex, string description) | |
1305 | + { | |
1306 | + if (description == null) | |
1307 | + return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); | |
1308 | + string[] nameStart; | |
1309 | + if (maxIndex == 1) | |
1310 | + nameStart = new string[]{"{0:", "{"}; | |
1311 | + else | |
1312 | + nameStart = new string[]{"{" + index + ":"}; | |
1313 | + for (int i = 0; i < nameStart.Length; ++i) { | |
1314 | + int start, j = 0; | |
1315 | + do { | |
1316 | + start = description.IndexOf (nameStart [i], j); | |
1317 | + } while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false); | |
1318 | + if (start == -1) | |
1319 | + continue; | |
1320 | + int end = description.IndexOf ("}", start); | |
1321 | + if (end == -1) | |
1322 | + continue; | |
1323 | + return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length); | |
1324 | + } | |
1325 | + return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); | |
1326 | + } | |
1327 | + | |
1328 | + private static string GetDescription (string description) | |
1329 | + { | |
1330 | + if (description == null) | |
1331 | + return string.Empty; | |
1332 | + StringBuilder sb = new StringBuilder (description.Length); | |
1333 | + int start = -1; | |
1334 | + for (int i = 0; i < description.Length; ++i) { | |
1335 | + switch (description [i]) { | |
1336 | + case '{': | |
1337 | + if (i == start) { | |
1338 | + sb.Append ('{'); | |
1339 | + start = -1; | |
1340 | + } | |
1341 | + else if (start < 0) | |
1342 | + start = i + 1; | |
1343 | + break; | |
1344 | + case '}': | |
1345 | + if (start < 0) { | |
1346 | + if ((i+1) == description.Length || description [i+1] != '}') | |
1347 | + throw new InvalidOperationException ("Invalid option description: " + description); | |
1348 | + ++i; | |
1349 | + sb.Append ("}"); | |
1350 | + } | |
1351 | + else { | |
1352 | + sb.Append (description.Substring (start, i - start)); | |
1353 | + start = -1; | |
1354 | + } | |
1355 | + break; | |
1356 | + case ':': | |
1357 | + if (start < 0) | |
1358 | + goto default; | |
1359 | + start = i + 1; | |
1360 | + break; | |
1361 | + default: | |
1362 | + if (start < 0) | |
1363 | + sb.Append (description [i]); | |
1364 | + break; | |
1365 | + } | |
1366 | + } | |
1367 | + return sb.ToString (); | |
1368 | + } | |
1369 | + | |
1370 | + private static IEnumerable<string> GetLines (string description) | |
1371 | + { | |
1372 | + if (string.IsNullOrEmpty (description)) { | |
1373 | + yield return string.Empty; | |
1374 | + yield break; | |
1375 | + } | |
1376 | + int length = 80 - OptionWidth - 1; | |
1377 | + int start = 0, end; | |
1378 | + do { | |
1379 | + end = GetLineEnd (start, length, description); | |
1380 | + char c = description [end-1]; | |
1381 | + if (char.IsWhiteSpace (c)) | |
1382 | + --end; | |
1383 | + bool writeContinuation = end != description.Length && !IsEolChar (c); | |
1384 | + string line = description.Substring (start, end - start) + | |
1385 | + (writeContinuation ? "-" : ""); | |
1386 | + yield return line; | |
1387 | + start = end; | |
1388 | + if (char.IsWhiteSpace (c)) | |
1389 | + ++start; | |
1390 | + length = 80 - OptionWidth - 2 - 1; | |
1391 | + } while (end < description.Length); | |
1392 | + } | |
1393 | + | |
1394 | + private static bool IsEolChar (char c) | |
1395 | + { | |
1396 | + return !char.IsLetterOrDigit (c); | |
1397 | + } | |
1398 | + | |
1399 | + private static int GetLineEnd (int start, int length, string description) | |
1400 | + { | |
1401 | + int end = System.Math.Min (start + length, description.Length); | |
1402 | + int sep = -1; | |
1403 | + for (int i = start+1; i < end; ++i) { | |
1404 | + if (description [i] == '\n') | |
1405 | + return i+1; | |
1406 | + if (IsEolChar (description [i])) | |
1407 | + sep = i+1; | |
1408 | + } | |
1409 | + if (sep == -1 || end == description.Length) | |
1410 | + return end; | |
1411 | + return sep; | |
1412 | + } | |
1413 | + } | |
1414 | +} | |
1415 | + |