OptionList.cs
上传用户:huiyue
上传日期:2022-04-08
资源大小:1429k
文件大小:14k
源码类别:

搜索引擎

开发平台:

ASP/ASPX

  1. //
  2. // OptionList.cs
  3. //
  4. // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
  5. //
  6. // (C) 2002 Rafael Teixeira
  7. //
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. // 
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. // 
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections;
  30. using System.IO;
  31. using System.Reflection;
  32. using System.Text;
  33. namespace Mono.GetOptions
  34. {
  35. /// <summary>
  36. /// Option Parsing
  37. /// </summary>
  38. public class OptionList
  39. {
  40. private Options optionBundle = null;
  41. private OptionsParsingMode parsingMode;
  42. private bool breakSingleDashManyLettersIntoManyOptions;
  43. private bool endOptionProcessingWithDoubleDash;
  44. public ErrorReporter ReportError;
  45. private string appExeName;
  46. private string appVersion;
  47. private string appTitle = "Add a [assembly: AssemblyTitle("Here goes the application name")] to your assembly";
  48. private string appCopyright = "Add a [assembly: AssemblyCopyright("(c)200n Here goes the copyright holder name")] to your assembly";
  49. private string appDescription = "Add a [assembly: AssemblyDescription("Here goes the short description")] to your assembly";
  50. private string appAboutDetails = "Add a [assembly: Mono.About("Here goes the short about details")] to your assembly";
  51. private string appUsageComplement = "Add a [assembly: Mono.UsageComplement("Here goes the usage clause complement")] to your assembly";
  52. private string appAdditionalInfo = null;
  53. private string appReportBugsTo = null;
  54. private string[] appAuthors;
  55.  
  56. private ArrayList list = new ArrayList();
  57. private ArrayList arguments = new ArrayList();
  58. private ArrayList argumentsTail = new ArrayList();
  59. private MethodInfo argumentProcessor = null;
  60. private bool HasSecondLevelHelp = false;
  61. internal bool MaybeAnOption(string arg)
  62. {
  63. return  ((parsingMode & OptionsParsingMode.Windows) > 0 && arg[0] == '/') || 
  64. ((parsingMode & OptionsParsingMode.Linux)   > 0 && arg[0] == '-');
  65. }
  66. public string Usage
  67. {
  68. get {
  69. return "Usage: " + appExeName + " [options] " + appUsageComplement;
  70. }
  71. }
  72. public string AboutDetails
  73. {
  74. get {
  75. return appAboutDetails;
  76. }
  77. }
  78. #region Assembly Attributes
  79. Assembly entry;
  80. private object[] GetAssemblyAttributes(Type type)
  81. {
  82. return entry.GetCustomAttributes(type, false);
  83. }
  84. private string[] GetAssemblyAttributeStrings(Type type)
  85. {
  86. object[] result = GetAssemblyAttributes(type);
  87. if ((result == null) || (result.Length == 0))
  88. return new string[0];
  89. int i = 0;
  90. string[] var = new string[result.Length];
  91. foreach(object o in result)
  92. var[i++] = o.ToString(); 
  93. return var;
  94. }
  95. private void GetAssemblyAttributeValue(Type type, string propertyName, ref string var)
  96. {
  97. object[] result = GetAssemblyAttributes(type);
  98. if ((result != null) && (result.Length > 0))
  99. var = (string)type.InvokeMember(propertyName, BindingFlags.Public | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance, null, result[0], new object [] {}); ;
  100. }
  101. private void GetAssemblyAttributeValue(Type type, ref string var)
  102. {
  103. object[] result = GetAssemblyAttributes(type);
  104. if ((result != null) && (result.Length > 0))
  105. var = result[0].ToString();
  106. }
  107. private void ExtractEntryAssemblyInfo(Type optionsType)
  108. {
  109. entry = optionsType.Assembly;
  110. if (entry == this.GetType().Assembly) {
  111. entry = Assembly.GetEntryAssembly();
  112. }
  113. appExeName = entry.GetName().Name;
  114. appVersion = entry.GetName().Version.ToString();
  115. GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute), "Title", ref appTitle);
  116. GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute), "Copyright", ref appCopyright);
  117. GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute), "Description", ref appDescription);
  118. GetAssemblyAttributeValue(typeof(Mono.AboutAttribute), ref appAboutDetails);
  119. GetAssemblyAttributeValue(typeof(Mono.UsageComplementAttribute), ref appUsageComplement);
  120. GetAssemblyAttributeValue(typeof(Mono.AdditionalInfoAttribute), ref appAdditionalInfo);
  121. GetAssemblyAttributeValue(typeof(Mono.ReportBugsToAttribute), ref appReportBugsTo);
  122. appAuthors = GetAssemblyAttributeStrings(typeof(AuthorAttribute));
  123. if (appAuthors.Length == 0) {
  124. appAuthors = new String[1];
  125. appAuthors[0] = "Add one or more [assembly: Mono.Author("Here goes the author name")] to your assembly";
  126. }
  127. }
  128. #endregion
  129. #region Constructors
  130. private void AddArgumentProcessor(MemberInfo memberInfo)
  131. {
  132. if (argumentProcessor != null)
  133. throw new NotSupportedException("More than one argument processor method found");
  134. if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo)) {
  135. if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(void).FullName)
  136. throw new NotSupportedException("Argument processor method must return 'void'");
  137. ParameterInfo[] parameters = ((MethodInfo)memberInfo).GetParameters();
  138. if ((parameters == null) || (parameters.Length != 1) || (parameters[0].ParameterType.FullName != typeof(string).FullName))
  139. throw new NotSupportedException("Argument processor method must have a string parameter");
  140. argumentProcessor = (MethodInfo)memberInfo; 
  141. }
  142. else
  143. throw new NotSupportedException("Argument processor marked member isn't a method");
  144. }
  145. public OptionList(Options optionBundle)
  146. {
  147. if (optionBundle == null)
  148. throw new ArgumentNullException("optionBundle");
  149. Type optionsType = optionBundle.GetType();
  150. this.optionBundle = optionBundle; 
  151. this.parsingMode = optionBundle.ParsingMode ;
  152. this.breakSingleDashManyLettersIntoManyOptions = optionBundle.BreakSingleDashManyLettersIntoManyOptions;
  153. this.endOptionProcessingWithDoubleDash = optionBundle.EndOptionProcessingWithDoubleDash;
  154. this.ReportError = optionBundle.ReportError;
  155. ExtractEntryAssemblyInfo(optionsType);
  156. foreach(MemberInfo mi in optionsType.GetMembers()) {
  157. object[] attribs = mi.GetCustomAttributes(typeof(KillOptionAttribute), true);
  158. if (attribs == null || attribs.Length == 0) {
  159. attribs = mi.GetCustomAttributes(typeof(OptionAttribute), true);
  160. if (attribs != null && attribs.Length > 0) {
  161. OptionDetails option = new OptionDetails(mi, (OptionAttribute)attribs[0], optionBundle);
  162. list.Add(option);
  163. HasSecondLevelHelp = HasSecondLevelHelp || option.SecondLevelHelp;
  164. } else if (mi.DeclaringType == mi.ReflectedType) { // not inherited
  165. attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true); 
  166. if (attribs != null && attribs.Length > 0)
  167. AddArgumentProcessor(mi);
  168. }
  169. }
  170. }
  171. if (argumentProcessor == null) // try to find an inherited one
  172. foreach(MemberInfo mi in optionsType.GetMembers()) 
  173. if (mi.DeclaringType != mi.ReflectedType) { // inherited
  174. object[] attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
  175. if (attribs != null && attribs.Length > 0)
  176. AddArgumentProcessor(mi);
  177. }
  178. }
  179. #endregion
  180. #region Prebuilt Options
  181. private bool bannerAlreadyShown = false;
  182. internal string AdditionalBannerInfo;
  183. public void ShowBanner()
  184. {
  185. if (!bannerAlreadyShown) {
  186. Console.WriteLine(appTitle + "  " + appVersion + " - " + appCopyright); 
  187. if (AdditionalBannerInfo != null)
  188. Console.WriteLine(AdditionalBannerInfo);
  189. }
  190. bannerAlreadyShown = true;
  191. }
  192. private void ShowTitleLines()
  193. {
  194. ShowBanner();
  195. Console.WriteLine(appDescription); 
  196. Console.WriteLine();
  197. }
  198. private void ShowAbout()
  199. {
  200. ShowTitleLines();
  201. Console.WriteLine(appAboutDetails); 
  202. Console.Write("Authors: ");
  203. Console.WriteLine(string.Join(", ", appAuthors));
  204. }
  205. private void ShowHelp(bool showSecondLevelHelp)
  206. {
  207. ShowTitleLines();
  208. Console.WriteLine(Usage);
  209. Console.WriteLine("Options:");
  210. ArrayList lines = new ArrayList(list.Count);
  211. int tabSize = 0;
  212. foreach (OptionDetails option in list)
  213. if (option.SecondLevelHelp == showSecondLevelHelp) {
  214. string[] optionLines = option.ToString().Split('n');
  215. foreach(string line in optionLines) {
  216. int pos = line.IndexOf('t');
  217. if (pos > tabSize)
  218. tabSize = pos;
  219. lines.Add(line);
  220. }
  221. }
  222. tabSize += 2;
  223. foreach (string line in lines) {
  224. string[] parts = line.Split('t');
  225. Console.Write(parts[0].PadRight(tabSize));
  226. Console.WriteLine(parts[1]);
  227. if (parts.Length > 2) {
  228. string spacer = new string(' ', tabSize);
  229. for(int i = 2; i < parts.Length; i++) {
  230. Console.Write(spacer);
  231. Console.WriteLine(parts[i]);
  232. }
  233. }
  234. }
  235. if (appAdditionalInfo != null)
  236. Console.WriteLine("n{0}", appAdditionalInfo);
  237. if (appReportBugsTo != null)
  238. Console.WriteLine("nPlease report bugs {0} <{1}>", (appReportBugsTo.IndexOf('@')>0)?"to":"at" , appReportBugsTo);
  239. }
  240. private void ShowUsage()
  241. {
  242. Console.WriteLine(Usage);
  243. Console.Write("Short Options: ");
  244. foreach (OptionDetails option in list)
  245. Console.Write(option.ShortForm.Trim());
  246. Console.WriteLine();
  247. }
  248. internal WhatToDoNext DoUsage()
  249. {
  250. ShowUsage();
  251. return WhatToDoNext.AbandonProgram;
  252. }
  253. internal WhatToDoNext DoAbout()
  254. {
  255. ShowAbout();
  256. return WhatToDoNext.AbandonProgram;
  257. }
  258. internal WhatToDoNext DoHelp()
  259. {
  260. ShowHelp(false);
  261. return WhatToDoNext.AbandonProgram;
  262. }
  263. internal WhatToDoNext DoHelp2()
  264. {
  265. ShowHelp(true);
  266. return WhatToDoNext.AbandonProgram;
  267. }
  268. #endregion
  269. #region Response File Expansion
  270. private void processResponseFileLine(string line, ArrayList result, StringBuilder sb)
  271. {
  272. int t = line.Length;
  273. for (int i = 0; i < t; i++) {
  274. char c = line [i];
  275. if (c == '"' || c == ''') {
  276. char end = c;
  277. for (i++; i < t; i++) {
  278. c = line [i];
  279. if (c == end)
  280. break;
  281. sb.Append(c);
  282. }
  283. } else if (c == ' ') {
  284. if (sb.Length > 0) {
  285. result.Add(sb.ToString());
  286. sb.Length = 0;
  287. }
  288. } else {
  289. sb.Append(c);
  290. }
  291. }
  292. if (sb.Length > 0) {
  293. result.Add(sb.ToString());
  294. sb.Length = 0;
  295. }
  296. }
  297. private void processResponseFile(string filename, ArrayList result)
  298. {
  299. StringBuilder sb = new StringBuilder();
  300. string line;
  301. try {
  302. using (StreamReader responseFile = new StreamReader(filename)) {
  303. while ((line = responseFile.ReadLine()) != null)
  304. processResponseFileLine(line, result, sb);
  305. responseFile.Close ();
  306. } catch (FileNotFoundException) {
  307. ReportError(2011, "Unable to find response file '" + filename + "'");
  308. } catch (Exception exception) {
  309. ReportError(2011, "Unable to open response file '" + filename + "'. " + exception.Message);
  310. }
  311. }
  312. private ArrayList ExpandResponseFiles(string[] args)
  313. {
  314. ArrayList result = new ArrayList();
  315. foreach(string arg in args)
  316. if (arg.StartsWith("@")) 
  317. processResponseFile(arg.Substring(1), result);
  318. else
  319. result.Add(arg);
  320. return result;
  321. }
  322. #endregion
  323. #region Arguments Processing
  324. private static int IndexOfAny(string where, params char[] what)
  325. {
  326. return where.IndexOfAny(what);
  327. }
  328. private string[] NormalizeArgs(string[] args)
  329. {
  330. bool ParsingOptions = true;
  331. ArrayList result = new ArrayList();
  332. foreach(string arg in ExpandResponseFiles(args)) {
  333. if (arg.Length > 0) {
  334. if (ParsingOptions) {
  335. if (endOptionProcessingWithDoubleDash && (arg == "--")) {
  336. ParsingOptions = false;
  337. continue;
  338. }
  339. if ((parsingMode & OptionsParsingMode.Linux) > 0 && 
  340.  arg[0] == '-' && arg.Length > 1 && arg[1] != '-' &&
  341.  breakSingleDashManyLettersIntoManyOptions) {
  342. foreach(char c in arg.Substring(1)) // many single-letter options
  343. result.Add("-" + c); // expand into individualized options
  344. continue;
  345. }
  346. if (MaybeAnOption(arg)) {
  347. int pos = IndexOfAny(arg, ':', '=');
  348. if(pos < 0)
  349. result.Add(arg);
  350. else {
  351. result.Add(arg.Substring(0, pos));
  352. result.Add(arg.Substring(pos+1));
  353. }
  354. continue;
  355. }
  356. } else {
  357. argumentsTail.Add(arg);
  358. continue;
  359. }
  360. // if nothing else matches then it get here
  361. result.Add(arg);
  362. }
  363. }
  364. return (string[])result.ToArray(typeof(string));
  365. }
  366. public string[] ProcessArgs(string[] args)
  367. {
  368. string arg;
  369. string nextArg;
  370. bool OptionWasProcessed;
  371. list.Sort();
  372. OptionDetails.LinkAlternatesInsideList(list);
  373. args = NormalizeArgs(args);
  374. try {
  375. int argc = args.Length;
  376. for (int i = 0; i < argc; i++) {
  377. arg =  args[i];
  378. if (i+1 < argc)
  379. nextArg = args[i+1];
  380. else
  381. nextArg = null;
  382. OptionWasProcessed = false;
  383. if (arg.Length > 1 && (arg.StartsWith("-") || arg.StartsWith("/"))) {
  384. foreach(OptionDetails option in list) {
  385. OptionProcessingResult result = option.ProcessArgument(arg, nextArg);
  386. if (result != OptionProcessingResult.NotThisOption) {
  387. OptionWasProcessed = true;
  388. if (result == OptionProcessingResult.OptionConsumedParameter)
  389. i++;
  390. break;
  391. }
  392. }
  393. }
  394. if (!OptionWasProcessed)
  395. ProcessNonOption(arg);
  396. }
  397. foreach(OptionDetails option in list)
  398. option.TransferValues(); 
  399. foreach(string argument in argumentsTail)
  400. ProcessNonOption(argument);
  401. return (string[])arguments.ToArray(typeof(string));
  402. } catch (Exception ex) {
  403. System.Console.WriteLine(ex.ToString());
  404. System.Environment.Exit(1);
  405. }
  406. return null;
  407. }
  408. private void ProcessNonOption(string argument)
  409. {
  410. if (optionBundle.VerboseParsingOfOptions)
  411. Console.WriteLine("argument [" + argument + "]");
  412. if (argumentProcessor == null)
  413. arguments.Add(argument);
  414. else
  415. argumentProcessor.Invoke(optionBundle, new object[] { argument });  
  416. }
  417. #endregion
  418. }
  419. }