You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
6.4 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace IPA
  6. {
  7. public class Arguments
  8. {
  9. public static readonly Arguments CmdLine = new Arguments(Environment.GetCommandLineArgs());
  10. private readonly List<string> positional = new List<string>();
  11. private readonly Dictionary<string, string> longFlags = new Dictionary<string, string>();
  12. private readonly Dictionary<char, string> flags = new Dictionary<char, string>();
  13. private readonly List<ArgumentFlag> flagObjects = new List<ArgumentFlag>();
  14. private string[] toParse;
  15. private Arguments(string[] args)
  16. {
  17. toParse = args.Skip(1).ToArray();
  18. }
  19. public Arguments Flags(params ArgumentFlag[] toAdd)
  20. {
  21. foreach (var f in toAdd) AddFlag(f);
  22. return this;
  23. }
  24. public void AddFlag(ArgumentFlag toAdd)
  25. {
  26. if (toParse == null) throw new InvalidOperationException();
  27. flagObjects.Add(toAdd);
  28. }
  29. public void Process()
  30. {
  31. foreach (var arg in toParse)
  32. {
  33. if (arg.StartsWith("--"))
  34. { // parse as a long flag
  35. var name = arg.Substring(2); // cut off first two chars
  36. string value = null;
  37. if (name.Contains('='))
  38. {
  39. var spl = name.Split('=');
  40. name = spl[0];
  41. value = string.Join("=", spl, 1, spl.Length - 1);
  42. }
  43. longFlags.Add(name, value);
  44. }
  45. else if (arg.StartsWith("-"))
  46. { // parse as flags
  47. var argument = arg.Substring(1); // cut off first char
  48. var subBuildState = new StringBuilder();
  49. var parsingValue = false;
  50. var mainChar = ' ';
  51. foreach (var chr in argument)
  52. {
  53. if (!parsingValue)
  54. {
  55. if (chr == '=')
  56. {
  57. parsingValue = true;
  58. }
  59. else
  60. {
  61. mainChar = chr;
  62. flags.Add(chr, null);
  63. }
  64. }
  65. else
  66. {
  67. if (chr == ',')
  68. {
  69. parsingValue = false;
  70. flags[mainChar] = subBuildState.ToString();
  71. subBuildState = new StringBuilder();
  72. }
  73. else
  74. {
  75. subBuildState.Append(chr);
  76. }
  77. }
  78. }
  79. if (parsingValue)
  80. {
  81. parsingValue = false;
  82. flags[mainChar] = subBuildState.ToString();
  83. subBuildState = new StringBuilder();
  84. }
  85. }
  86. else
  87. { // parse as positional
  88. positional.Add(arg);
  89. }
  90. }
  91. toParse = null;
  92. foreach (var flag in flagObjects)
  93. {
  94. foreach (var charFlag in flag.ShortFlags)
  95. {
  96. if (!(flag.exists_ = HasFlag(charFlag))) continue;
  97. flag.value_ = GetFlagValue(charFlag);
  98. goto FoundValue; // continue to next flagObjects item
  99. }
  100. foreach (var longFlag in flag.LongFlags)
  101. {
  102. if (!(flag.exists_ = HasLongFlag(longFlag))) continue;
  103. flag.value_ = GetLongFlagValue(longFlag);
  104. goto FoundValue; // continue to next flagObjects item
  105. }
  106. FoundValue:;
  107. }
  108. }
  109. public bool HasLongFlag(string flag)
  110. {
  111. return longFlags.ContainsKey(flag);
  112. }
  113. public bool HasFlag(char flag)
  114. {
  115. return flags.ContainsKey(flag);
  116. }
  117. public string GetLongFlagValue(string flag)
  118. {
  119. return longFlags[flag];
  120. }
  121. public string GetFlagValue(char flag)
  122. {
  123. return flags[flag];
  124. }
  125. public void PrintHelp()
  126. {
  127. const string indent = " ";
  128. var filename = Environment.GetCommandLineArgs()[0];
  129. const string format = @"usage:
  130. {2}{0} [FLAGS] [ARGUMENTS]
  131. flags:
  132. {1}";
  133. var flagsBuilder = new StringBuilder();
  134. foreach (var flag in flagObjects)
  135. {
  136. flagsBuilder.AppendFormat("{2}{0}{3}{1}",
  137. string.Join(", ", flag.ShortFlags.Select(s => $"-{s}").Concat( flag.LongFlags.Select(s => $"--{s}")) ),
  138. Environment.NewLine, indent, flag.ValueString != null ? "=" + flag.ValueString : "");
  139. flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent);
  140. }
  141. Console.Write(format, filename, flagsBuilder, indent);
  142. }
  143. public IReadOnlyList<string> PositionalArgs => positional;
  144. }
  145. public class ArgumentFlag
  146. {
  147. internal readonly List<char> ShortFlags = new List<char>();
  148. internal readonly List<string> LongFlags = new List<string>();
  149. internal string value_;
  150. internal bool exists_;
  151. public ArgumentFlag(params string[] flags)
  152. {
  153. foreach (var part in flags)
  154. AddPart(part);
  155. }
  156. private void AddPart(string flagPart)
  157. {
  158. if (flagPart.StartsWith("--"))
  159. LongFlags.Add(flagPart.Substring(2));
  160. else if (flagPart.StartsWith("-"))
  161. ShortFlags.Add(flagPart[1]);
  162. }
  163. public bool Exists => exists_;
  164. public string Value => value_;
  165. public bool HasValue => Exists && Value != null;
  166. public string DocString { get; set; } = "";
  167. public string ValueString { get; set; }
  168. public static implicit operator bool(ArgumentFlag f)
  169. {
  170. return f.Exists;
  171. }
  172. }
  173. }