* Filters/FilterPackage.cs, Filters/FilterRPM.cs,
[beagle.git] / Util / CommandLineFu.cs
blobbdda5c1db785d7c932d5739ad4021dc041cd0825
1 //
2 // CommandLineFu.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.Reflection;
30 using System.Text;
32 namespace CommandLineFu {
34 [AttributeUsage (AttributeTargets.Field)]
35 public class OptionAttribute : Attribute {
36 public string Name;
37 public string LongName;
38 public string Description = "(no description)";
39 public string ArgDescription = "arg";
42 internal class Processor {
44 object target_object;
46 public Processor (object target_object)
48 this.target_object = target_object;
51 ///////////////////////////////////////////////////////////////////
53 private class Pair {
55 public FieldInfo Field;
56 public OptionAttribute Option;
58 private object target_object;
59 private bool touched = false;
62 public Pair (object target)
64 target_object = target;
67 public string Name {
68 get { return Field.Name; }
71 public string OptionName {
72 get {
73 if (Option.LongName != null)
74 return "--" + Option.LongName;
75 else
76 return "-" + Option.Name;
80 public string UsageName {
81 get {
82 StringBuilder builder = new StringBuilder ();
83 if (Option.LongName != null) {
84 builder.Append ("--");
85 builder.Append (Option.LongName);
87 if (Option.Name != null)
88 builder.Append (", ");
90 if (Option.Name != null) {
91 builder.Append ("-");
92 builder.Append (Option.Name);
94 if (Field.FieldType != typeof (System.Boolean)) {
95 builder.Append (" [");
96 builder.Append (Option.ArgDescription);
97 builder.Append ("]");
99 return builder.ToString ();
103 public bool Touched {
104 get { return touched; }
107 public object Value {
108 get { return Field.GetValue (target_object); }
111 // Return true if value was actually used
112 public bool Set (string value)
114 touched = true;
116 // Deal with bools first, since they are easy.
117 if (Field.FieldType == typeof (System.Boolean)) {
118 Field.SetValue (target_object, true);
119 return false;
122 object parsed_value = null;
124 if (Field.FieldType == typeof (System.String)) {
126 parsed_value = value;
128 } else if (Field.FieldType == typeof (System.Int32)) {
130 try {
131 parsed_value = System.Int32.Parse (value);
132 } catch (Exception ex) {
133 // parsed_value will still be null, so the
134 // "Couldn't parse..." error will be displayed.
137 } else if (Field.FieldType == typeof (System.Double)) {
139 try {
140 parsed_value = System.Double.Parse (value);
141 } catch (Exception ex) { }
144 if (parsed_value != null) {
145 Field.SetValue (target_object, parsed_value);
146 } else {
147 Console.WriteLine ("Couldn't parse '{0}' as {1}", value, Field.FieldType);
150 return true;
154 ArrayList all_pairs = new ArrayList ();
155 Hashtable by_name = new Hashtable ();
156 Hashtable by_long_name = new Hashtable ();
158 public void AddOption (FieldInfo field, OptionAttribute option)
160 Pair pair = new Pair (target_object);
161 pair.Field = field;
162 pair.Option = option;
164 all_pairs.Add (pair);
166 if (option.Name != null)
167 by_name [option.Name] = pair;
169 if (option.LongName != null)
170 by_long_name [option.LongName] = pair;
173 private static bool IsOption (string arg)
175 return arg.StartsWith ("-") || arg.StartsWith ("/");
178 private Pair ParseOption (string arg, out string next_arg)
180 next_arg = null;
182 char [] separator_chars = new char [] { '=', ':' };
183 int i = arg.IndexOfAny (separator_chars);
184 if (i != -1) {
185 next_arg = arg.Substring (i+1);
186 arg = arg.Substring (0, i);
189 string stripped_arg = null;
190 if (arg.StartsWith ("/")) {
191 stripped_arg = arg.Substring (1);
192 } else if (arg.StartsWith ("-")) {
193 int pos = 1;
194 while (pos < arg.Length && arg [pos] == '-')
195 ++pos;
196 stripped_arg = arg.Substring (pos);
199 Pair pair = null;
200 pair = by_long_name [stripped_arg] as Pair;
201 if (pair == null)
202 pair = by_name [stripped_arg] as Pair;
204 return pair;
207 ///////////////////////////////////////////////////////////////////
209 public void Spew ()
211 foreach (Pair pair in all_pairs) {
212 Console.WriteLine ("DEBUG {0}: {1}={2} {3}",
213 pair.OptionName,
214 pair.Name,
215 pair.Value,
216 pair.Touched ? "" : "(default)");
220 ///////////////////////////////////////////////////////////////////
222 public void SpewVersion ()
224 Console.WriteLine (CommandLine.ProgramVersion != null ? CommandLine.ProgramVersion : "unknown");
227 public void SpewBanner ()
229 if (CommandLine.ProgramName == null)
230 return;
231 Console.Write (CommandLine.ProgramName);
232 if (CommandLine.ProgramVersion != null) {
233 Console.Write (" ");
234 Console.Write (CommandLine.ProgramVersion);
236 if (CommandLine.ProgramDate != null) {
237 Console.Write (" - ");
238 Console.Write (CommandLine.ProgramDate);
240 Console.WriteLine ();
242 if (CommandLine.ProgramCopyright != null)
243 Console.WriteLine (CommandLine.ProgramCopyright);
247 public void SpewOptionDocs ()
249 int max_usage_name_len = 0;
251 // FIXME: This need better formatting, wrapping of
252 // description lines, etc.
253 // It should also be put in a sane order.
255 Console.WriteLine ("Options:");
257 foreach (Pair pair in all_pairs) {
258 int len = pair.UsageName.Length;
259 if (len > max_usage_name_len)
260 max_usage_name_len = len;
263 foreach (Pair pair in all_pairs) {
264 StringBuilder builder = new StringBuilder ();
265 string usage_name = pair.UsageName;
266 builder.Append (" ");
267 builder.Append (usage_name);
268 builder.Append (' ', max_usage_name_len - usage_name.Length);
269 builder.Append (" ");
270 builder.Append (pair.Option.Description);
272 Console.WriteLine (builder.ToString ());
276 ///////////////////////////////////////////////////////////////////
278 private string [] TheRealWork (string [] args)
280 ArrayList parameters = new ArrayList ();
282 int i = 0;
283 bool parameters_only = false;
284 while (i < args.Length) {
285 string arg = args [i];
286 ++i;
288 string next_arg = null;
289 if (i < args.Length)
290 next_arg = args [i];
292 if (parameters_only || ! IsOption (arg)) {
293 parameters.Add (arg);
294 } else if (arg == "--") {
295 parameters_only = true;
296 } else {
297 string attached_next_arg = null;
298 Pair pair = ParseOption (arg, out attached_next_arg);
300 if (pair == null) {
301 Console.WriteLine ("Ignoring unknown argument '{0}'", arg);
302 } else if (attached_next_arg != null) {
303 if (! pair.Set (attached_next_arg)) {
304 // FIXME: If we didn't use the attached arg, something must be wrong.
305 // Throw an exception?
306 Console.WriteLine ("FIXME: Didn't use attached arg '{0}' on {1}",
307 attached_next_arg,
308 pair.OptionName);
310 } else {
311 if (pair.Set (next_arg))
312 ++i;
317 // If we ended prematurely, treat everything that is left
318 // as a parameter.
319 while (i < args.Length)
320 parameters.Add (args [i]);
322 // Convert the list of parameters to an array and return it.
323 string [] parameter_array = new string [parameters.Count];
324 for (int j = 0; j < parameters.Count; ++j)
325 parameter_array [j] = parameters [j] as string;
326 return parameter_array;
329 public string [] Process (string [] args)
331 foreach (string arg in args) {
332 // FIXME: These should be displayed in the banner information.
333 if (arg == "--version") {
334 SpewVersion ();
335 return null;
336 } else if (arg == "--help") {
337 SpewBanner ();
338 SpewOptionDocs ();
339 return null;
343 string [] parameters = TheRealWork (args);
345 if (CommandLine.Debug) {
346 Spew ();
347 for (int i = 0; i < parameters.Length; ++i)
348 Console.WriteLine ("DEBUG Param {0}: {1}", i, parameters [i]);
351 return parameters;
355 public class CommandLine {
357 static public bool Debug = false;
359 static public string ProgramName = null;
360 static public string ProgramVersion = null;
361 static public string ProgramDate = null;
362 static public string ProgramCopyright = null;
363 static public string ProgramHomePage = null;
365 static private Processor BuildProcessor (Type type, object obj, BindingFlags flags)
367 Processor proc = new Processor (obj);
369 flags |= BindingFlags.NonPublic;
370 flags |= BindingFlags.Public;
372 FieldInfo [] field_info_array = type.GetFields (flags);
373 foreach (FieldInfo fi in field_info_array) {
375 object [] attributes = fi.GetCustomAttributes (true);
376 foreach (object attr in attributes) {
377 OptionAttribute option_attr = attr as OptionAttribute;
378 if (option_attr != null)
379 proc.AddOption (fi, option_attr);
383 return proc;
386 static public string [] Process (object obj, string [] args)
388 Processor proc = BuildProcessor (obj.GetType (), obj, BindingFlags.Instance);
389 return proc.Process (args);
392 static public string [] Process (Type t, string [] args)
394 Processor proc = BuildProcessor (t, null, BindingFlags.Static);
395 return proc.Process (args);
402 #if false
403 class CommandLineFu_SampleCode {
405 [Option (Name="f",
406 LongName="foo",
407 Description="The Foo Option",
408 ArgDescription="FOOARG")]
409 static private string foo = "foo_default";
411 [Option (LongName="bar",
412 Description="The Bar Option")]
413 static private int bar = 12345;
415 [Option (LongName="baz",
416 Description="The Baz Option")]
417 static private bool baz = false;
419 [Option (Name="d",
420 Description="As you might expect, the d option")]
421 static private double d = 3.14159;
423 static void Main (string [] args)
425 CommandLine.Debug = true;
426 CommandLine.Process (typeof (CommandLineFu_SampleCode), args);
429 #endif