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.

199 lines
6.1 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. }
  80. else
  81. { // parse as positional
  82. positional.Add(arg);
  83. }
  84. }
  85. toParse = null;
  86. foreach (var flag in flagObjects)
  87. {
  88. foreach (var charFlag in flag.ShortFlags)
  89. {
  90. if (!(flag.exists_ = HasFlag(charFlag))) continue;
  91. flag.value_ = GetFlagValue(charFlag);
  92. goto FoundValue; // continue to next flagObjects item
  93. }
  94. foreach (var longFlag in flag.LongFlags)
  95. {
  96. if (!(flag.exists_ = HasLongFlag(longFlag))) continue;
  97. flag.value_ = GetLongFlagValue(longFlag);
  98. goto FoundValue; // continue to next flagObjects item
  99. }
  100. FoundValue:;
  101. }
  102. }
  103. public bool HasLongFlag(string flag)
  104. {
  105. return longFlags.ContainsKey(flag);
  106. }
  107. public bool HasFlag(char flag)
  108. {
  109. return flags.ContainsKey(flag);
  110. }
  111. public string GetLongFlagValue(string flag)
  112. {
  113. return longFlags[flag];
  114. }
  115. public string GetFlagValue(char flag)
  116. {
  117. return flags[flag];
  118. }
  119. public void PrintHelp()
  120. {
  121. const string indent = " ";
  122. var filename = Environment.GetCommandLineArgs()[0];
  123. const string format = @"usage:
  124. {2}{0} [FLAGS] [ARGUMENTS]
  125. flags:
  126. {1}";
  127. var flagsBuilder = new StringBuilder();
  128. foreach (var flag in flagObjects)
  129. {
  130. flagsBuilder.AppendFormat("{2}{0}{3}{1}",
  131. string.Join(", ", flag.ShortFlags.Select(s => $"-{s}").Concat( flag.LongFlags.Select(s => $"--{s}")) ),
  132. Environment.NewLine, indent, flag.ValueString != null ? "=" + flag.ValueString : "");
  133. flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent);
  134. }
  135. Console.Write(format, filename, flagsBuilder, indent);
  136. }
  137. public IReadOnlyList<string> PositionalArgs => positional;
  138. }
  139. public class ArgumentFlag
  140. {
  141. internal readonly List<char> ShortFlags = new List<char>();
  142. internal readonly List<string> LongFlags = new List<string>();
  143. internal string value_;
  144. internal bool exists_;
  145. public ArgumentFlag(params string[] flags)
  146. {
  147. foreach (var part in flags)
  148. AddPart(part);
  149. }
  150. private void AddPart(string flagPart)
  151. {
  152. if (flagPart.StartsWith("--"))
  153. LongFlags.Add(flagPart.Substring(2));
  154. else if (flagPart.StartsWith("-"))
  155. ShortFlags.Add(flagPart[1]);
  156. }
  157. public bool Exists => exists_;
  158. public string Value => value_;
  159. public bool HasValue => Exists && Value != null;
  160. public string DocString { get; set; } = "";
  161. public string ValueString { get; set; }
  162. public static implicit operator bool(ArgumentFlag f)
  163. {
  164. return f.Exists;
  165. }
  166. }
  167. }