Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions Commander.NET/Models/RawArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Commander.NET.Models
{
internal class RawArguments<T>
internal class RawArguments<T>
{
BindingFlags bindingFlags;
HashSet<string> booleanKeys = new HashSet<string>();
Expand Down Expand Up @@ -55,32 +55,43 @@ internal string GetMatchingKey(IEnumerable<string> keys)
internal List<string> GetPositionalArguments()
{
return new List<string>(positionalArguments);
}

}
internal RawArguments<T> Parse(string[] args, Separators separators)
{
List<string> commands = Utils.GetCommandNames<T>(bindingFlags);

for (int i = 0; i < args.Length; i++)
{
if (string.IsNullOrWhiteSpace(args[i]))
string arg = args[i];
if (string.IsNullOrWhiteSpace(arg))
continue;
if ((arg.Matches(@"^-[a-zA-Z0-9_]=") || arg.Matches(@"^--[a-zA-Z0-9_-]{2,}=")) && separators.HasFlag(Separators.Equals))
{
string pair = arg.TrimStart('-');
int pos = pair.IndexOf('=');
string key = pair.Substring(0, pos);
string value = pair.Substring(pos + 1);
Console.WriteLine(key + "=" + value);

if ((args[i].Matches(@"^-[a-zA-Z0-9_]=\w+$") || args[i].Matches(@"^--[a-zA-Z0-9_-]{2,}=\w+$")) && separators.HasFlag(Separators.Equals)
|| (args[i].Matches(@"^-[a-zA-Z0-9_]:\w+$") || args[i].Matches(@"^--[a-zA-Z0-9_-]{2,}:\w+$")) && separators.HasFlag(Separators.Colon))
TryAddKeyValuePair(key, value);
}
else if ((arg.Matches(@"^-[a-zA-Z0-9_]:") || arg.Matches(@"^--[a-zA-Z0-9_-]{2,}:")) && separators.HasFlag(Separators.Colon))
{
string key = args[i].TrimStart('-').Split(':')[0].Split('=')[0];
string value = args[i].Split(':').Last().Split('=').Last();
string pair = arg.TrimStart('-');
int pos = pair.IndexOf(':');
string key = pair.Substring(0, pos);
string value = pair.Substring(pos + 1);

TryAddKeyValuePair(key, value);
}
else if (args[i].Matches(@"^-[a-zA-Z0-9_]$") || args[i].Matches(@"^--[a-zA-Z0-9_-]{2,}$"))
else if (arg.Matches(@"^-[a-zA-Z0-9_]$") || arg.Matches(@"^--[a-zA-Z0-9_-]{2,}$"))
{
string key = args[i].TrimStart('-');
string key = arg.TrimStart('-');

int intTest;
if (!booleanKeys.Contains(key) && i < args.Length - 1
&& (!args[i + 1].StartsWith("-") || int.TryParse(args[i+1], out intTest)))
if (!booleanKeys.Contains(key) && i < args.Length - 1
&& (!args[i + 1].StartsWith("-") || int.TryParse(args[i + 1], out intTest)))
{
TryAddKeyValuePair(key, args[i + 1]);
i++;
Expand All @@ -90,33 +101,33 @@ internal RawArguments<T> Parse(string[] args, Separators separators)
flags.Add(key);
}
}
else if (args[i].Matches(@"^-[a-zA-Z0-9_]{2,}$"))
else if (arg.Matches(@"^-[a-zA-Z0-9_]{2,}$"))
{
// Multiple flags
flags.AddRange(
args[i].ToCharArray().Select(c => c.ToString())
arg.ToCharArray().Select(c => c.ToString())
);
}
else
{
if (commands.Contains(args[i]))
if (commands.Contains(arg))
{
// We caught a command name, stop parsing
Command = args[i];
Command = arg;
CommandIndex = i;

return this;
}
else
{
// No commands with this name, add it to the positional arguments
positionalArguments.Add(args[i]);
positionalArguments.Add(arg);
}
}
}
return this;
}

}
internal bool GetBoolean(string key)
{
return flags.Contains(key) || keyValuePairs.ContainsKey(key);
Expand Down
232 changes: 227 additions & 5 deletions Commander.NET/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Commander.NET
{
internal static class Utils
internal static class Utils
{
internal static IEnumerable<MemberInfo> GetParameterMembers<T, Q>(BindingFlags flags) where Q : Attribute
{
Expand Down Expand Up @@ -101,8 +101,8 @@ internal static MemberInfo GetCommandWithName<T>(string name, BindingFlags bindi
return member;
}
return null;
}

}
/*
internal static string[] SplitArgumentsLine(string line)
{
List<string> args = new List<string>();
Expand Down Expand Up @@ -155,8 +155,230 @@ internal static string[] SplitArgumentsLine(string line)

reset();

return args.Where(a => !string.IsNullOrWhiteSpace(a))
.ToArray();
return args.Where(a => !string.IsNullOrWhiteSpace(a)).ToArray();
}
*/
internal static string[] SplitArgumentsLine(string line)
{
return CommandLineToArgvW("echo " + line).Skip(1).ToArray();
}

/**
* C# equivalent of CommandLineToArgvW
* Translated from https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/shcore/main.c#l264
*/
public static string[] CommandLineToArgvW(string cmdline)
{
if ((cmdline = cmdline.Trim()).Length == 0)
{
return new string[0];
}
int len = cmdline.Length;
int argc = 0;
int i = 0;
char s = cmdline[i];
char END = '\0';
/* The first argument, the executable path, follows special rules */
argc = 1;
if (s == '"')
{
do
{
s = ++i < len ? cmdline[i] : END;
if (s == '"')
break;
} while (s != END);
}
else
{
while (s != END && s != ' ' && s != '\t')
{
s = ++i < len ? cmdline[i] : END;
}
}
/* skip to the first argument, if any */
while (s == ' ' || s == '\t')
s = ++i < len ? cmdline[i] : END;
if (s != END)
argc++;

/* Analyze the remaining arguments */
int qcount = 0; // quote count
int bcount = 0; // backslash count
while (i < len)
{
s = cmdline[i];
if ((s == ' ' || s == '\t') && qcount == 0)
{
/* skip to the next argument and count it if any */
do
{
s = ++i < len ? cmdline[i] : END;
} while (s == ' ' || s == '\t');
if (s != END)
argc++;
bcount = 0;
}
else if (s == '\\')
{
/* '\', count them */
bcount++;
s = ++i < len ? cmdline[i] : END;
}
else if (s == '"')
{
/* '"' */
if ((bcount & 1) == 0)
qcount++; /* unescaped '"' */
s = ++i < len ? cmdline[i] : END;
bcount = 0;
/* consecutive quotes, see comment in copying code below */
while (s == '"')
{
qcount++;
s = ++i < len ? cmdline[i] : END;
}
qcount = qcount % 3;
if (qcount == 2)
qcount = 0;
}
else
{
/* a regular character */
bcount = 0;
s = ++i < len ? cmdline[i] : END;
}
}
string[] argv = new string[argc];
StringBuilder sb = new StringBuilder();
i = 0;
int j = 0;
s = cmdline[i];
if (s == '"')
{
do
{
s = ++i < len ? cmdline[i] : END;
if (s == '"')
break;
else
sb.Append(s);
} while (s != END);
argv[j++] = sb.ToString();
sb.Clear();
}
else
{
while (s != END && s != ' ' && s != '\t')
{
sb.Append(s);
s = ++i < len ? cmdline[i] : END;
}
argv[j++] = sb.ToString();
sb.Clear();
}
while (s == ' ' || s == '\t')
s = ++i < len ? cmdline[i] : END;
if (i >= len)
return argv;
qcount = 0;
bcount = 0;
while (i < len)
{
if ((s == ' ' || s == '\t') && qcount == 0)
{
/* close the argument */
argv[j++] = sb.ToString();
sb.Clear();
bcount = 0;
/* skip to the next one and initialize it if any */
do
{
s = ++i < len ? cmdline[i] : END;
} while (s == ' ' || s == '\t');
}
else if (s == '\\')
{
sb.Append(s);
s = ++i < len ? cmdline[i] : END;
bcount++;
}
else if (s == '"')
{
if ((bcount & 1) == 0)
{
/* Preceded by an even number of '\', this is half that number of '\', plus a quote which we erase. */
sb.Length -= bcount / 2;
qcount++;
}
else
{
/* Preceded by an odd number of '\', this is half that number of '\' followed by a '"' */
sb.Length = (sb.Length - 1) - bcount / 2 - 1;
sb.Append('"');
}
s = ++i < len ? cmdline[i] : END;
bcount = 0;
/* Now count the number of consecutive quotes. Note that qcount
* already takes into account the opening quote if any, as well as
* the quote that lead us here.
*/
while (s == '"')
{
if (++qcount == 3)
{
sb.Append('"');
qcount = 0;
}
s = ++i < len ? cmdline[i] : END;
}
if (qcount == 2)
qcount = 0;
}
else
{
/* a regular character */
sb.Append(s);
s = ++i < len ? cmdline[i] : END;
bcount = 0;
}
}
if (sb.Length > 0)
{
argv[j++] = sb.ToString();
sb.Clear();
}
return argv;
}

/**
* Windows native CommandLineToArgvW
* Copied from https://stackoverflow.com/questions/298830/split-string-containing-command-line-parameters-into-string-in-c-sharp#answer-749653
*/
/*
[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
public static string[] CommandLineToArgvW(string commandLine)
{
int argc;
IntPtr argv = CommandLineToArgvW(commandLine, out argc);
if (argv == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
try
{
string[] args = new string[argc];
for (int i = 0; i < argc; i++)
{
IntPtr p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args;
}
finally
{
Marshal.FreeHGlobal(argv);
}
}
*/
}
}