1159 lines
42 KiB
Plaintext
1159 lines
42 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Parameter.cs
|
|
//
|
|
// Note:
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Collections;
|
|
|
|
using Microsoft.SingSharp;
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Xml;
|
|
using Microsoft.Singularity.V1.Services;
|
|
using System.Globalization;
|
|
|
|
namespace Microsoft.Singularity.Applications
|
|
{
|
|
[CLSCompliant(false)]
|
|
public class ParameterProcessor
|
|
{
|
|
private Parameter[] parameterSet;
|
|
private Parameter[] positionSet;
|
|
private bool havePositional;
|
|
private string appName;
|
|
private int totalCount;
|
|
private string helpMsg;
|
|
private int boolCount, longCount, stringCount;
|
|
private bool hasStringArray;
|
|
private int stringArrayIndex;
|
|
|
|
public ParameterProcessor()
|
|
{
|
|
}
|
|
|
|
private enum pType {
|
|
boolType,
|
|
longType,
|
|
stringType,
|
|
stringArrayType
|
|
}
|
|
|
|
private class Parameter
|
|
{
|
|
public string Name;
|
|
public bool Mandatory;
|
|
public string Default;
|
|
public int Position;
|
|
public int Index;
|
|
public pType Kind;
|
|
public bool Resolved;
|
|
public string HelpMsg;
|
|
|
|
public bool boolValue;
|
|
public string stringValue;
|
|
public long longValue;
|
|
public string[] stringArrayValue;
|
|
|
|
public Parameter(pType kind) {
|
|
Resolved = false;
|
|
Name = null;
|
|
Kind = kind;
|
|
}
|
|
}
|
|
|
|
private string CodeToString(ParameterCode code)
|
|
{
|
|
switch (code) {
|
|
case ParameterCode.Success:
|
|
return "Success";
|
|
break;
|
|
case ParameterCode.OutOfRange:
|
|
return "OutOfRange";
|
|
break;
|
|
case ParameterCode.NotSet:
|
|
return "NotSet";
|
|
break;
|
|
case ParameterCode.Retrieved:
|
|
return "Retrieved";
|
|
break;
|
|
case ParameterCode.Undefined:
|
|
return "Undefined";
|
|
break;
|
|
default:
|
|
return "This should never happen";
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private bool StringToBool (string arg, out bool value)
|
|
{
|
|
|
|
value = false;
|
|
bool ok = false;
|
|
if (arg == null) return false;
|
|
|
|
string s = arg.ToLower();
|
|
|
|
if (s == "true" || s == "t" || s == "1") {
|
|
value = true;
|
|
ok = true;
|
|
}
|
|
|
|
if (s == "false" || s == "f" || s == "0") {
|
|
value = false;
|
|
ok = true;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
private bool StringToLong (string arg, out long value)
|
|
{
|
|
value = -1;
|
|
int start = 0;
|
|
bool doHex = false;
|
|
|
|
if (arg == null) return false;
|
|
if (arg.StartsWith("0x") || arg.StartsWith("0X")) {
|
|
if (arg.Length == 2) return false;
|
|
doHex = true;
|
|
start = 2;
|
|
}
|
|
else {
|
|
for (int i = start; i < arg.Length; i++) {
|
|
if (!Char.IsNumber(arg[i]) && arg[i] != '-') return false;
|
|
}
|
|
}
|
|
try {
|
|
if (doHex) value = System.Int64.Parse(arg, NumberStyles.AllowHexSpecifier);
|
|
else value = System.Int64.Parse(arg);
|
|
}
|
|
catch (Exception e) {
|
|
Console.WriteLine("Exception: converting {0} to Long. error={1}",arg,e.Message);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private Parameter! GetXmlAttributes(XmlNode! p, pType t)
|
|
{
|
|
|
|
Parameter a = new Parameter(t);
|
|
assert a != null;
|
|
|
|
a.Position = p.GetAttribute("Position", -1);
|
|
a.Index = p.GetAttribute("id", -1);
|
|
a.Mandatory = p.GetAttribute("Mandatory", false);
|
|
a.Name = p.GetAttribute("Name",null);
|
|
a.Default = p.GetAttribute("Default",null);
|
|
a.HelpMsg = p.GetAttribute("HelpMessage",null);
|
|
|
|
//Console.WriteLine("name=({0}), Mandatory={1}, id={2}, position={3}",
|
|
// a.Name, a.Mandatory, a.Index, a.Position);
|
|
return a;
|
|
}
|
|
|
|
private static bool InSeparators(char c, char []! separators)
|
|
{
|
|
for (int i = 0; i < separators.Length; i++) {
|
|
if (separators[i] == c)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
private static ArrayList! Tokenize(string! input, int last,
|
|
char []! separators)
|
|
{
|
|
ArrayList tokens = new ArrayList();
|
|
|
|
for (int i = 0; i <= last;) {
|
|
// Skip separators
|
|
while (i <= last && InSeparators(input[i], separators)) {
|
|
i++;
|
|
}
|
|
|
|
if (i > last)
|
|
break;
|
|
|
|
// Try to slurp word
|
|
int start = i;
|
|
while (i <= last &&
|
|
!InSeparators(input[i], separators) &&
|
|
input[i] != '\'')
|
|
{
|
|
i++;
|
|
}
|
|
if (i != start) {
|
|
tokens.Add(input.Substring(start, i - start));
|
|
}
|
|
|
|
// Skip separators
|
|
while (i <= last && InSeparators(input[i], separators)) {
|
|
i++;
|
|
}
|
|
|
|
if (i > last)
|
|
break;
|
|
|
|
// Try to quoted slurp word
|
|
if (input[i] == '\'') {
|
|
start = i;
|
|
i++;
|
|
while (i <= last && input[i] != '\'') {
|
|
i++;
|
|
}
|
|
|
|
if (i <= last && input[i] == '\'') {
|
|
tokens.Add(input.Substring(start + 1, i - start - 1));
|
|
i++;
|
|
}
|
|
else {
|
|
tokens.Add(input.Substring(start, i - start));
|
|
i++;
|
|
}
|
|
}
|
|
} // end for
|
|
|
|
return tokens;
|
|
}
|
|
|
|
private Parameter FindParameter(string name)
|
|
{
|
|
if (parameterSet != null) {
|
|
for (int i = 0; i < parameterSet.Length; i++) {
|
|
Parameter a = parameterSet[i];
|
|
if (a == null) continue;
|
|
assert a.Name != null;
|
|
if (a.Name == name) return parameterSet[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool ParseAndSetParameter(Parameter! a, string! value)
|
|
{
|
|
a.Resolved = false;
|
|
|
|
switch (a.Kind) {
|
|
case pType.boolType:
|
|
if (value == "") {
|
|
// Allow for "-x" to mean "-x=true".
|
|
a.Resolved = true;
|
|
a.boolValue = String.Compare(a.Default, "true", true) == 0;
|
|
}
|
|
else {
|
|
a.Resolved = StringToBool(value, out a.boolValue);
|
|
}
|
|
break;
|
|
case pType.longType:
|
|
a.Resolved = StringToLong(value, out a.longValue);
|
|
break;
|
|
case pType.stringType:
|
|
a.stringValue = value;
|
|
a.Resolved = true;
|
|
break;
|
|
}
|
|
|
|
if(a.Resolved) {
|
|
return true;
|
|
}
|
|
else {
|
|
if(a.Kind == pType.boolType) {
|
|
Console.WriteLine("{0} for {1} is not a bool value.", value, a.Name);
|
|
}
|
|
else if(a.Kind == pType.longType) {
|
|
Console.WriteLine("{0} for {1} is not a integer value.", value, a.Name);
|
|
}
|
|
else {
|
|
Console.WriteLine("Unknown type for {0}", a.Name);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private string! KindToString(pType t)
|
|
{
|
|
switch (t) {
|
|
case pType.boolType:
|
|
return "bool";
|
|
case pType.longType:
|
|
return "int";
|
|
case pType.stringType:
|
|
return "string";
|
|
case pType.stringArrayType:
|
|
return "strings";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
#if false
|
|
public void Show(string action, Manifest! manifest, bool verbose)
|
|
{
|
|
if (action != null) {
|
|
Console.Write("\n {0} @{1} ", appName, action);
|
|
InitializeParameters(action, manifest);
|
|
ShowArgs(action, manifest);
|
|
return;
|
|
}
|
|
|
|
XmlNode catNode = manifest.GetCategoriesNode();
|
|
if (catNode == null) {
|
|
Console.WriteLine(" unable to find categories node!");
|
|
return;
|
|
}
|
|
ArrayList actions = new ArrayList();
|
|
|
|
assert catNode != null;
|
|
foreach (XmlNode n in catNode.Children) {
|
|
assert n != null;
|
|
string s = n.GetAttribute("Action", null);
|
|
actions.Add(s);
|
|
}
|
|
|
|
if (actions.Count > 0) {
|
|
Console.Write("\n {0} Actions: ", appName);
|
|
for (int i = 0; i < actions.Count; i++) {
|
|
Console.Write("{0} ",actions[i]);
|
|
}
|
|
Console.WriteLine("\n");
|
|
}
|
|
|
|
if (verbose) {
|
|
for (int i = 0; i < actions.Count; i++) {
|
|
string a = (string) actions[i];
|
|
if (a != null ) Console.Write("\n Configuration Parameters for action @{0}",a);
|
|
InitializeParameters(a, manifest);
|
|
ShowArgs(action, manifest);
|
|
}
|
|
}
|
|
else {
|
|
// Show a reasonably-short list of commands.
|
|
ShowCommandsSummary(manifest);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// This method shows a list of all of the commands (actions) that an
|
|
/// application provides, as defined in its manifest. The list simply
|
|
/// shows the names of each command, and the helptext for each.
|
|
/// </summary>
|
|
void ShowCommandsSummary(Manifest! manifest)
|
|
{
|
|
XmlNode catNode = manifest.GetCategoriesNode();
|
|
if (catNode == null) {
|
|
Console.WriteLine(" unable to find categories node!");
|
|
return;
|
|
}
|
|
|
|
const string format = "{0,-16} {1}";
|
|
|
|
bool any = false;
|
|
|
|
foreach (XmlNode node in catNode.Children) {
|
|
assert node != null;
|
|
|
|
if (node.Name != "category")
|
|
continue;
|
|
|
|
string action;
|
|
string actionWithAt;
|
|
|
|
// Is it the default action?
|
|
string value = node.GetAttribute("DefaultAction", null);
|
|
bool isDefault = value != null && value.Length != 0;
|
|
if (isDefault) {
|
|
action = "";
|
|
actionWithAt = "";
|
|
}
|
|
else {
|
|
action = node.GetAttribute("Action", null);
|
|
if (action == null)
|
|
continue;
|
|
actionWithAt = "@" + action;
|
|
}
|
|
|
|
string! help = GetHelpMessage(node);
|
|
|
|
if (!any) {
|
|
any = true;
|
|
Console.WriteLine("The program supports the following actions:");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
actionWithAt = this.appName + " " + actionWithAt;
|
|
|
|
const int max_help_length = 60;
|
|
|
|
if (help.Length > max_help_length) {
|
|
string[]! help_lines = BreakLineAtWords(help, max_help_length);
|
|
bool first = true;
|
|
foreach (string! help_line in help_lines) {
|
|
if (first) {
|
|
Console.WriteLine(format, actionWithAt, help_line);
|
|
first = false;
|
|
}
|
|
else {
|
|
Console.WriteLine(format, "", help_line);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Console.WriteLine(format, actionWithAt, help);
|
|
}
|
|
}
|
|
|
|
if (!any) {
|
|
Console.WriteLine("The program does not support any commands.");
|
|
}
|
|
else {
|
|
Console.WriteLine();
|
|
// Console.WriteLine("Use -?? for verbose info on all actions.");
|
|
Console.WriteLine("Use '@cmd -?' for detailed help on a particular command.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method breaks a string into a list of strings, each of which is at most 'maxlength'
|
|
/// in length, if possible. The method tries to break the string at whitespace boundaries.
|
|
/// Note that it is possible for entries returned to be longer than 'maxlength', so don't
|
|
/// rely on that behavior.
|
|
/// </summary>
|
|
string[]! BreakLineAtWords(string! line, int maxlength)
|
|
{
|
|
if (line.Length < maxlength)
|
|
return new string[] { line };
|
|
|
|
ArrayList list = new ArrayList();
|
|
|
|
int pos = 0;
|
|
for (;;) {
|
|
if (pos >= line.Length)
|
|
break;
|
|
|
|
int remaining = line.Length - pos;
|
|
if (remaining <= maxlength) {
|
|
// No need to break anymore.
|
|
list.Add(line.Substring(pos, remaining));
|
|
break;
|
|
}
|
|
|
|
int len = maxlength;
|
|
assert len < remaining;
|
|
|
|
// Move backward and find the first whitespace char.
|
|
while (len > 0 && !Char.IsWhiteSpace(line[pos + len - 1]))
|
|
len--;
|
|
|
|
// Remove whitespace from the end of the current line.
|
|
while (len > 0 && Char.IsWhiteSpace(line[pos + len - 1]))
|
|
len--;
|
|
|
|
if (len == 0) {
|
|
// Uh oh. The current line is one big long non-breaking string.
|
|
// Just break it at maxlength. Ugly.
|
|
len = maxlength;
|
|
}
|
|
|
|
list.Add(line.Substring(pos, len));
|
|
|
|
pos += len;
|
|
|
|
// Skip whitespace.
|
|
while (pos < line.Length && Char.IsWhiteSpace(line[pos]))
|
|
pos++;
|
|
}
|
|
|
|
Array obj_array = list.ToArray(typeof(string));
|
|
return (string[])obj_array;
|
|
}
|
|
|
|
string! GetHelpMessage(XmlNode! actionNode)
|
|
{
|
|
string value = actionNode.GetAttribute("HelpMessage", null);
|
|
if (value != null)
|
|
return value;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method shows a summary of all of the commands (actions) defined in
|
|
/// an application manifest.
|
|
/// </summary>
|
|
void ShowCommandsSummaryWithArgs(Manifest! manifest)
|
|
{
|
|
// Show a reasonably-short list of commands.
|
|
|
|
string[] actions = GetAllActions(manifest);
|
|
|
|
if (actions.Length > 0) {
|
|
foreach (string action in actions) {
|
|
InitializeParameters(action, manifest);
|
|
|
|
string! line = GetParameterSyntaxSummary(manifest, action);
|
|
Console.WriteLine(line);
|
|
|
|
if (this.helpMsg != null) {
|
|
string! help = this.helpMsg;
|
|
const int max_length = 70;
|
|
if (help.Length > max_length) {
|
|
string[]! help_lines = BreakLineAtWords(help, max_length);
|
|
for (int i = 0; i < help_lines.Length; i++) {
|
|
Console.WriteLine(" " + help_lines[i]);
|
|
}
|
|
}
|
|
else {
|
|
Console.WriteLine(" " + this.helpMsg);
|
|
}
|
|
Console.WriteLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("Use '" + this.appName + " @cmd -?' for detailed help on a particular command.");
|
|
Console.WriteLine("Use '" + this.appName + " -??' for detailed help on all commands.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds a
|
|
/// </summary>
|
|
string! GetParameterSyntaxSummary(Manifest! manifest, string action)
|
|
{
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
if (action != null) {
|
|
buffer.Append(appName);
|
|
buffer.Append(" @");
|
|
buffer.Append(action);
|
|
}
|
|
else {
|
|
buffer.Append(appName);
|
|
}
|
|
|
|
// First pass, show positional arguments.
|
|
|
|
if (positionSet != null)
|
|
{
|
|
foreach (Parameter p in positionSet) {
|
|
if (p == null)
|
|
continue;
|
|
|
|
buffer.Append(' ');
|
|
|
|
if (!p.Mandatory)
|
|
buffer.Append('[');
|
|
|
|
buffer.Append('<');
|
|
buffer.Append(p.Name);
|
|
buffer.Append('>');
|
|
|
|
if (!p.Mandatory)
|
|
buffer.Append(']');
|
|
}
|
|
}
|
|
|
|
// Next, show non-positional arguments (-foo=x)
|
|
if (parameterSet != null) {
|
|
foreach (Parameter p in parameterSet) {
|
|
if (p == null)
|
|
continue;
|
|
|
|
// Positional parameters are apparently in both lists.
|
|
// Screen them out.
|
|
bool skip = false;
|
|
if (positionSet != null) {
|
|
foreach (Parameter positional in positionSet) {
|
|
if (positional == p) {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (skip)
|
|
continue;
|
|
|
|
buffer.Append(' ');
|
|
|
|
if (!p.Mandatory)
|
|
buffer.Append('[');
|
|
|
|
buffer.Append('-');
|
|
buffer.Append(p.Name);
|
|
|
|
switch (p.Kind) {
|
|
case pType.boolType:
|
|
break;
|
|
|
|
case pType.longType:
|
|
buffer.Append("=nn");
|
|
break;
|
|
|
|
default:
|
|
buffer.Append("=...");
|
|
break;
|
|
}
|
|
|
|
if (!p.Mandatory)
|
|
buffer.Append(']');
|
|
}
|
|
}
|
|
|
|
return buffer.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds a list of all of the actions (commands) defined in a manifest.
|
|
/// Note: This CAN return a null entry (an array containing a null entry),
|
|
/// if the program has a default action, and most do.
|
|
/// </summary>
|
|
string[]! GetAllActions(Manifest! manifest)
|
|
{
|
|
XmlNode catNode = manifest.GetCategoriesNode();
|
|
if (catNode == null) {
|
|
Console.WriteLine(" unable to find categories node!");
|
|
return new string[0];
|
|
}
|
|
ArrayList actions = new ArrayList();
|
|
|
|
foreach (XmlNode n in catNode.Children) {
|
|
assert n != null;
|
|
string s = n.GetAttribute("Action", null);
|
|
actions.Add(s);
|
|
}
|
|
|
|
Array actions_array = actions.ToArray(typeof(string));
|
|
return (string[])actions_array;
|
|
}
|
|
|
|
void ShowVerboseCommandHelpAll(Manifest! manifest)
|
|
{
|
|
string[]! actions = GetAllActions(manifest);
|
|
|
|
foreach (string action in actions) {
|
|
InitializeParameters(action, manifest);
|
|
ShowVerboseCommandHelp(manifest, action);
|
|
Console.WriteLine();
|
|
}
|
|
}
|
|
|
|
///
|
|
//<summary>
|
|
// This method shows detailed help for a specific command.
|
|
// It shows the command syntax, and information on each
|
|
// parameter.
|
|
//</summary>
|
|
//
|
|
void ShowVerboseCommandHelp(Manifest! manifest, string action)
|
|
{
|
|
Console.WriteLine("Help for command: " + action);
|
|
|
|
InitializeParameters(action, manifest);
|
|
|
|
if (this.helpMsg != null) {
|
|
Console.WriteLine();
|
|
string[]! lines = BreakLineAtWords(this.helpMsg, 70);
|
|
foreach (string line in lines) {
|
|
Console.WriteLine(" " + line);
|
|
}
|
|
}
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Usage:");
|
|
string! syntax_summary = GetParameterSyntaxSummary(manifest, action);
|
|
Console.WriteLine(" " + syntax_summary);
|
|
|
|
Console.WriteLine();
|
|
|
|
// Now show parameter details.
|
|
|
|
if (parameterSet != null && parameterSet.Length != 0) {
|
|
Console.WriteLine("Parameters:");
|
|
Console.WriteLine();
|
|
|
|
// 0 = parameter name
|
|
// 1 = parameter type
|
|
// 2 = mandatory?
|
|
// 3 = help text
|
|
const string parameter_format = "{0,-10} {1,-8} {2,-6} {3}";
|
|
|
|
Console.WriteLine(parameter_format, "Name", "Type", "Req'd?", "Description");
|
|
Console.WriteLine(parameter_format, "====", "====", "======", "===========");
|
|
|
|
if (parameterSet != null) {
|
|
foreach (Parameter a in parameterSet) {
|
|
if (a == null)
|
|
continue;
|
|
|
|
string! typestring = KindToString(a.Kind);
|
|
const int max_length = 40;
|
|
string! help = a.HelpMsg != null ? a.HelpMsg : "";
|
|
if (help.Length > max_length) {
|
|
string[]! help_lines = BreakLineAtWords(help, max_length);
|
|
for (int i = 0; i < help_lines.Length; i++) {
|
|
if (i == 0)
|
|
Console.WriteLine(parameter_format, a.Name, typestring, a.Mandatory ? "yes" : "no", help_lines[i]);
|
|
else
|
|
Console.WriteLine(parameter_format, "", "", "", help_lines[i]);
|
|
}
|
|
}
|
|
else {
|
|
Console.WriteLine(parameter_format, a.Name, typestring, a.Mandatory ? "yes" : "no", a.HelpMsg != null ? a.HelpMsg : "");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Console.WriteLine("\nExternal Channels needed:");
|
|
// manifest.SetEndpoints(null, action, true);
|
|
}
|
|
|
|
///<summary>
|
|
/// Parse the command line and return the appName, action, and strongly typed parameter values.
|
|
/// manifest is used to deduce the strong type information for the parameters.
|
|
///</summary>
|
|
///<return>
|
|
/// True if a new process should be created.
|
|
/// There are several reasons this function may return false, including:
|
|
/// invalid action,
|
|
/// -? or -?? for help information only,
|
|
/// missing required parameters,
|
|
/// unknown parameter name,
|
|
/// incorrect parameter format,
|
|
///</return>
|
|
private bool Parse(String[]! cmd,
|
|
Manifest! manifest,
|
|
out string appName,
|
|
out string action,
|
|
out Parameter[] parameters)
|
|
{
|
|
appName = null;
|
|
action = null;
|
|
parameters = null;
|
|
|
|
if(cmd == null || cmd.Length == 0 || cmd[0] == null) {
|
|
Console.WriteLine("Invalid command line");
|
|
return false;
|
|
}
|
|
|
|
appName = (!)cmd[0];
|
|
string[] args = cmd;
|
|
|
|
// see if we have an action verb. If so determine if it is valid.
|
|
// if it is valid prime the parameter set based on it.
|
|
if (cmd.Length > 1 && cmd[1] != null && ((!)cmd[1]).Length >= 1
|
|
&& ((!)cmd[1])[0] == '@') {
|
|
// we have an action.
|
|
action = ((!)cmd[1]).Substring(1);
|
|
InitializeParameters(action, manifest);
|
|
args = new String[cmd.Length -1];
|
|
Array.Copy(cmd, 1, args, 0, cmd.Length-1);
|
|
}
|
|
else {
|
|
InitializeParameters(null, manifest);
|
|
}
|
|
|
|
if (args.Length > 1 && args[1] != null && args[1] == "-?") {
|
|
if (action != null) {
|
|
ShowVerboseCommandHelp(manifest, action);
|
|
}
|
|
else {
|
|
ShowCommandsSummaryWithArgs(manifest);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (args.Length > 1 && args[1] != null && args[1] == "-??"){
|
|
ShowVerboseCommandHelpAll(manifest);
|
|
return false;
|
|
}
|
|
|
|
ArrayList extras = new ArrayList();
|
|
ArrayList parameterList = new ArrayList();
|
|
bool seenPositional = false;
|
|
int positionalBase = 0;
|
|
|
|
for (int i=1; i < args.Length ; i++){
|
|
Parameter param = null;
|
|
string arg = args[i];
|
|
assert arg != null;
|
|
if (arg.Length>0 && arg[0] == '-') {
|
|
// this is an optional arg
|
|
arg = arg.Substring(1);
|
|
if (arg == null || arg == "") {
|
|
Console.WriteLine("'-' with no parameter name is invalid");
|
|
return false;
|
|
}
|
|
ArrayList tokens = Tokenize(arg, arg.Length - 1, new char[] {'=', ':'});
|
|
string name = (string)tokens[0];
|
|
param = FindParameter(name);
|
|
if (param != null) {
|
|
if (tokens.Count == 1) {
|
|
//this is potentially a bool parameter with no value
|
|
if (param.Kind == pType.boolType) {
|
|
param.boolValue = true;
|
|
param.Resolved = true;
|
|
}
|
|
else {
|
|
Console.WriteLine("invalid parameter assignment");
|
|
return false;
|
|
}
|
|
}
|
|
else if (tokens.Count == 2) {
|
|
bool ok = ParseAndSetParameter(param, (string)(!)tokens[1]);
|
|
if (!ok) return false;
|
|
}
|
|
else {
|
|
Console.WriteLine("invalid parameter assignment");
|
|
return false;
|
|
}
|
|
parameterList.Add(param);
|
|
}
|
|
else {
|
|
//unknown parameter
|
|
Console.WriteLine("Unknown parameter not set");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (!seenPositional) {
|
|
seenPositional = true;
|
|
positionalBase = i;
|
|
}
|
|
assert i - positionalBase >= 0;
|
|
if (positionSet != null && (i - positionalBase) < positionSet.Length) {
|
|
param = positionSet[i - positionalBase];
|
|
}
|
|
if (param != null) {
|
|
bool ok = ParseAndSetParameter(param, arg);
|
|
if (!ok) return false;
|
|
parameterList.Add(param);
|
|
}
|
|
else {
|
|
if (hasStringArray) {
|
|
// add it to the Array List
|
|
// After processing all the tokens the list will be
|
|
// converted to StringArray parameter if present
|
|
extras.Add(args[i]);
|
|
}
|
|
else {
|
|
Console.WriteLine("{0} is not a known parameter.", arg);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check to see if all positional args were resolved
|
|
if (positionSet != null) {
|
|
for (int i=0; i < positionSet.Length; i++) {
|
|
Parameter param = positionSet[i];
|
|
if (param != null && ! param.Resolved && param.Mandatory) {
|
|
Console.WriteLine("Mandatory positional Parameter {0} at {1} not set.",
|
|
param.Name, i);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check to see if all mandatory args were resolved
|
|
if (parameterSet != null) {
|
|
for (int i=0; i < parameterSet.Length; i++) {
|
|
Parameter param = parameterSet[i];
|
|
assert param != null;
|
|
assert param.Name != null;
|
|
if (param.Mandatory && ! param.Resolved) {
|
|
Console.WriteLine("Mandatory Parameter {0} not set.", param.Name);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set default values if needed on non mandatory parameters
|
|
if (parameterSet != null) {
|
|
for (int i=0; i < parameterSet.Length; i++ ) {
|
|
Parameter param = parameterSet[i];
|
|
assert param != null;
|
|
assert param.Name != null;
|
|
// ignore defaults for string array type..
|
|
if (param.Kind != pType.stringArrayType
|
|
&& !param.Resolved) {
|
|
ParseAndSetParameter(param, param.Default != null ? param.Default : "");
|
|
parameterList.Add(param);
|
|
}
|
|
}
|
|
}
|
|
|
|
// is there a StringArrayParameter defined in the manifest?
|
|
// If so pass the extras to the process
|
|
if (hasStringArray) {
|
|
// note if hasStringArray is true, then parameterSet is guaranteed to be non-null
|
|
Parameter! param = (!)((!)parameterSet)[stringArrayIndex];
|
|
param.stringArrayValue = (string[])extras.ToArray(typeof(string));
|
|
parameterList.Add(param);
|
|
}
|
|
|
|
parameters = (Parameter[])parameterList.ToArray(typeof(Parameter));
|
|
return true;
|
|
}
|
|
|
|
///<summary>
|
|
/// Parse the command line into stronly typed parameters using the manifest,
|
|
/// validate the parameters, and create the process if all parameters
|
|
/// are in the right format.
|
|
///</summary>
|
|
///<return>
|
|
/// True if process is created and paramters are parsed,
|
|
/// validated, and set successfully.
|
|
///</return>
|
|
public bool ProcessParameters(string[] commandLine,
|
|
Manifest manifest,
|
|
out Process process,
|
|
out string action)
|
|
{
|
|
process = null;
|
|
action = null;
|
|
if (manifest == null) return false;
|
|
if (commandLine == null) return false;
|
|
|
|
string appName;
|
|
Parameter[] parameters;
|
|
if(! Parse(commandLine, manifest, out appName, out action, out parameters)) {
|
|
Console.WriteLine("Failed to parse command line");
|
|
return false;
|
|
}
|
|
|
|
process = new Process((!)appName, action, null);
|
|
if (process == null) {
|
|
Console.WriteLine("Unable to create process {0} with action={1}",appName,action);
|
|
return false;
|
|
}
|
|
|
|
if (parameters != null) {
|
|
for(int i = 0; i < parameters.Length; i++) {
|
|
Parameter! param = (!)parameters[i];
|
|
ParameterCode code = ParameterCode.Undefined;
|
|
|
|
switch(param.Kind) {
|
|
case pType.boolType:
|
|
code = process.SetStartupBoolArg(param.Index, param.boolValue);
|
|
break;
|
|
case pType.longType:
|
|
code = process.SetStartupLongArg(param.Index, param.longValue);
|
|
break;
|
|
case pType.stringType:
|
|
code = process.SetStartupStringArg(param.Index, param.stringValue);
|
|
break;
|
|
case pType.stringArrayType:
|
|
string[] stringArray = param.stringArrayValue == null ?
|
|
new string[] {} : param.stringArrayValue;
|
|
code = process.SetStartupStringArrayArg(0, stringArray);
|
|
break;
|
|
}
|
|
|
|
if (code != ParameterCode.Success){
|
|
Console.WriteLine("unable to set {0} index {1} to {2} for arg {3}. error={4}",
|
|
param.Kind, param.Index, param.boolValue, param.Name, CodeToString(code) );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///<summary>
|
|
/// Set the position for the parameter.
|
|
///</summary>
|
|
private void SetPosition(Parameter! p)
|
|
{
|
|
if (p != null && p.Position != -1) {
|
|
if (p.Position < 0 || p.Position > totalCount-1) {
|
|
string error = string.Format("{0} position index out of range ({1})",
|
|
p.Name, p.Position);
|
|
throw new ManifestException(error);
|
|
}
|
|
else if (positionSet != null) {
|
|
positionSet[p.Position] = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
///<summary>
|
|
/// Use the manifest to initialize the parameters without values.
|
|
/// Later ProcessParameters() will fill in the values from the command line.
|
|
///
|
|
/// Throws ManifestException upon error.
|
|
///</summary>
|
|
public void InitializeParameters(string actionName, Manifest! manifest)
|
|
{
|
|
XmlNode bools;
|
|
XmlNode strings;
|
|
XmlNode stringArrays;
|
|
XmlNode longs;
|
|
XmlNode ActionNode;
|
|
int pIndex;
|
|
|
|
longCount = 0;
|
|
boolCount = 0;
|
|
stringCount = 0;
|
|
hasStringArray = false;
|
|
stringArrayIndex = 0;
|
|
int stringArrayCount = 0;
|
|
|
|
bools = null;
|
|
longs = null;
|
|
strings = null;
|
|
stringArrays = null;
|
|
parameterSet = null;
|
|
positionSet = null;
|
|
|
|
// Use Action to determine which set of parameters are going to be used.
|
|
// If action is null we need to ensure there is 1 Parameter configuration
|
|
// and its DefaultAction parameter is set to true.
|
|
XmlNode actionNode = manifest.GetCategoryNode(actionName);
|
|
|
|
if (actionNode == null) {
|
|
string error = actionName != null ?
|
|
string.Format("Unknown action {0} recognized", actionName) :
|
|
"Unable to find default action";
|
|
throw new ManifestException(error);
|
|
}
|
|
|
|
string categoryName = actionNode.GetAttribute("name","");
|
|
|
|
if (categoryName != "console") {
|
|
throw new ManifestException(string.Format(
|
|
"This manifest's category is not 'console': ({0})", categoryName));
|
|
}
|
|
|
|
bools = manifest.GetBoolParameterNode(actionNode);
|
|
longs = manifest.GetLongParameterNode(actionNode);
|
|
strings = manifest.GetStringParameterNode(actionNode);
|
|
stringArrays = manifest.GetStringArrayParameterNode(actionNode);
|
|
helpMsg = manifest.GetHelpMessage(actionNode);
|
|
|
|
if (bools == null && longs == null && strings == null && stringArrays == null) {
|
|
parameterSet = new Parameter[0];
|
|
positionSet = new Parameter[0];
|
|
return;
|
|
}
|
|
|
|
if (bools != null) {
|
|
boolCount = bools.GetAttribute("length", 0);
|
|
}
|
|
|
|
if (longs != null) {
|
|
longCount = longs.GetAttribute("length", 0);
|
|
}
|
|
|
|
if (strings != null) {
|
|
stringCount = strings.GetAttribute("length", 0);
|
|
}
|
|
|
|
if (stringArrays != null) {
|
|
stringArrayCount = stringArrays.GetAttribute("length", 0);
|
|
if (stringArrayCount > 1) {
|
|
throw new ManifestException("Cannot support more than one string array parameters");
|
|
}
|
|
else if (stringArrayCount == 1) {
|
|
hasStringArray = true;
|
|
}
|
|
}
|
|
|
|
totalCount = stringCount + stringArrayCount + longCount + boolCount;
|
|
|
|
parameterSet = new Parameter[totalCount];
|
|
positionSet = new Parameter[totalCount];
|
|
if (totalCount == 0 ) return;
|
|
|
|
assert positionSet != null;
|
|
assert parameterSet != null;
|
|
|
|
pIndex = 0;
|
|
if (bools != null) {
|
|
if (boolCount > 0) {
|
|
foreach (XmlNode! bp in bools.Children) {
|
|
if ( bp.Name == "BoolParameter") {
|
|
Parameter p = GetXmlAttributes(bp, pType.boolType);
|
|
SetPosition(p);
|
|
parameterSet[pIndex++] = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (longs != null) {
|
|
if (longCount > 0) {
|
|
foreach (XmlNode! lp in longs.Children) {
|
|
if ( lp.Name == "LongParameter") {
|
|
Parameter p = GetXmlAttributes(lp, pType.longType);
|
|
SetPosition(p);
|
|
parameterSet[pIndex++] = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strings != null) {
|
|
if (stringCount > 0) {
|
|
foreach (XmlNode! sp in strings.Children) {
|
|
if ( sp.Name == "StringParameter") {
|
|
Parameter p = GetXmlAttributes(sp, pType.stringType);
|
|
SetPosition(p);
|
|
parameterSet[pIndex++] = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stringArrays != null) {
|
|
if (stringArrayCount > 0) {
|
|
foreach (XmlNode! sp in stringArrays.Children) {
|
|
if ( sp.Name == "StringArrayParameter") {
|
|
Parameter p = GetXmlAttributes(sp, pType.stringArrayType);
|
|
SetPosition(p);
|
|
stringArrayIndex = pIndex;
|
|
parameterSet[pIndex++] = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (positionSet != null && positionSet[0] != null) {
|
|
havePositional = true;
|
|
bool seenNull = false;
|
|
for (int i = 0; i < positionSet.Length; i++) {
|
|
if (positionSet[i] == null) {
|
|
seenNull = true;
|
|
continue;
|
|
}
|
|
if (seenNull) {
|
|
StringBuilder buffer = new StringBuilder(64);
|
|
buffer.Append("Positional indices not contiguous! ");
|
|
for (int j = 0; j < positionSet.Length; j++) {
|
|
Parameter p = positionSet[j];
|
|
if (p != null ) {
|
|
buffer.AppendFormat("{0}({1}) ",j, p.Name);
|
|
}
|
|
}
|
|
throw new ManifestException(buffer.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|