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.

214 lines
6.7 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 escaped = false;
  51. var mainChar = ' ';
  52. foreach (var chr in argument)
  53. {
  54. if (!parsingValue)
  55. {
  56. if (chr == '=')
  57. {
  58. parsingValue = true;
  59. }
  60. else
  61. {
  62. mainChar = chr;
  63. flags.Add(chr, null);
  64. }
  65. }
  66. else
  67. {
  68. if (!escaped)
  69. {
  70. if (chr == ',')
  71. {
  72. parsingValue = false;
  73. flags[mainChar] = subBuildState.ToString();
  74. subBuildState = new StringBuilder();
  75. continue;
  76. }
  77. else if (chr == '\\')
  78. {
  79. escaped = true;
  80. continue;
  81. }
  82. }
  83. subBuildState.Append(chr);
  84. }
  85. }
  86. if (parsingValue)
  87. {
  88. parsingValue = false;
  89. flags[mainChar] = subBuildState.ToString();
  90. subBuildState = new StringBuilder();
  91. }
  92. }
  93. else
  94. { // parse as positional
  95. positional.Add(arg);
  96. }
  97. }
  98. toParse = null;
  99. foreach (var flag in flagObjects)
  100. {
  101. foreach (var charFlag in flag.ShortFlags)
  102. {
  103. if (!(flag.exists_ = HasFlag(charFlag))) continue;
  104. flag.value_ = GetFlagValue(charFlag);
  105. goto FoundValue; // continue to next flagObjects item
  106. }
  107. foreach (var longFlag in flag.LongFlags)
  108. {
  109. if (!(flag.exists_ = HasLongFlag(longFlag))) continue;
  110. flag.value_ = GetLongFlagValue(longFlag);
  111. goto FoundValue; // continue to next flagObjects item
  112. }
  113. FoundValue:;
  114. }
  115. }
  116. public bool HasLongFlag(string flag)
  117. {
  118. return longFlags.ContainsKey(flag);
  119. }
  120. public bool HasFlag(char flag)
  121. {
  122. return flags.ContainsKey(flag);
  123. }
  124. public string GetLongFlagValue(string flag)
  125. {
  126. return longFlags[flag];
  127. }
  128. public string GetFlagValue(char flag)
  129. {
  130. return flags[flag];
  131. }
  132. public void PrintHelp()
  133. {
  134. const string indent = " ";
  135. var filename = Environment.GetCommandLineArgs()[0];
  136. const string format = @"usage:
  137. {2}{0} [FLAGS] [ARGUMENTS]
  138. flags:
  139. {1}";
  140. var flagsBuilder = new StringBuilder();
  141. foreach (var flag in flagObjects)
  142. {
  143. flagsBuilder.AppendFormat("{2}{0}{3}{1}",
  144. string.Join(", ", flag.ShortFlags.Select(s => $"-{s}").Concat( flag.LongFlags.Select(s => $"--{s}")) ),
  145. Environment.NewLine, indent, flag.ValueString != null ? "=" + flag.ValueString : "");
  146. flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent);
  147. }
  148. Console.Write(format, filename, flagsBuilder, indent);
  149. }
  150. public IReadOnlyList<string> PositionalArgs => positional;
  151. }
  152. public class ArgumentFlag
  153. {
  154. internal readonly List<char> ShortFlags = new List<char>();
  155. internal readonly List<string> LongFlags = new List<string>();
  156. internal string value_;
  157. internal bool exists_;
  158. public ArgumentFlag(params string[] flags)
  159. {
  160. foreach (var part in flags)
  161. AddPart(part);
  162. }
  163. private void AddPart(string flagPart)
  164. {
  165. if (flagPart.StartsWith("--"))
  166. LongFlags.Add(flagPart.Substring(2));
  167. else if (flagPart.StartsWith("-"))
  168. ShortFlags.Add(flagPart[1]);
  169. }
  170. public bool Exists => exists_;
  171. public string Value => value_;
  172. public bool HasValue => Exists && Value != null;
  173. public string DocString { get; set; } = "";
  174. public string ValueString { get; set; }
  175. public static implicit operator bool(ArgumentFlag f)
  176. {
  177. return f.Exists;
  178. }
  179. }
  180. }