30 using System.Collections.Generic;
31 using System.Diagnostics;
33 using System.Reflection;
35 using System.Text.RegularExpressions;
36 using System.Threading;
38 using OpenSim.Framework;
49 private class CommandInfo
64 public string help_text;
69 public string long_help;
74 public string descriptive_help;
79 public List<CommandDelegate> fn;
82 public const string GeneralHelpText
83 =
"To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n";
85 public const string ItemHelpText
86 =
@"For more information, type 'help all' to get a list of all commands,
87 or type help <item>' where <item> is one of the following:";
92 private Dictionary<string, object> tree =
93 new Dictionary<string, object>();
98 private Dictionary<string, List<CommandInfo>> m_modulesCommands =
new Dictionary<string, List<CommandInfo>>();
107 List<string> help =
new List<string>();
108 List<string> helpParts =
new List<string>(cmd);
111 helpParts.RemoveAt(0);
116 if (helpParts.Count == 0)
118 help.Add(GeneralHelpText);
119 help.Add(ItemHelpText);
120 help.AddRange(CollectModulesHelp(tree));
122 else if (helpParts.Count == 1 && helpParts[0] ==
"all")
124 help.AddRange(CollectAllCommandsHelp());
128 help.AddRange(CollectHelp(helpParts));
140 private List<string> CollectAllCommandsHelp()
142 List<string> help =
new List<string>();
144 lock (m_modulesCommands)
146 foreach (List<CommandInfo> commands
in m_modulesCommands.Values)
148 var ourHelpText = commands.ConvertAll(c => string.Format(
"{0} - {1}", c.help_text, c.long_help));
149 help.AddRange(ourHelpText);
163 private List<string> CollectHelp(List<string> helpParts)
165 string originalHelpRequest = string.Join(
" ", helpParts.ToArray());
166 List<string> help =
new List<string>();
169 if (TryCollectModuleHelp(originalHelpRequest, help))
171 help.Insert(0, ItemHelpText);
175 Dictionary<string, object> dict = tree;
176 while (helpParts.Count > 0)
178 string helpPart = helpParts[0];
180 if (!dict.ContainsKey(helpPart))
185 if (dict[helpPart] is Dictionary<string, Object>)
186 dict = (Dictionary<string, object>)dict[helpPart];
188 helpParts.RemoveAt(0);
192 if (dict.ContainsKey(
String.Empty))
194 CommandInfo commandInfo = (CommandInfo)dict[
String.Empty];
195 help.Add(commandInfo.help_text);
196 help.Add(commandInfo.long_help);
198 string descriptiveHelp = commandInfo.descriptive_help;
201 if (descriptiveHelp !=
string.Empty)
202 help.Add(string.Empty);
204 help.Add(commandInfo.descriptive_help);
208 help.Add(string.Format(
"No help is available for {0}", originalHelpRequest));
220 private bool TryCollectModuleHelp(
string moduleName, List<string> helpText)
222 lock (m_modulesCommands)
224 foreach (
string key in m_modulesCommands.Keys)
227 if (moduleName.ToLower() == key.ToLower())
229 List<CommandInfo> commands = m_modulesCommands[
key];
230 var ourHelpText = commands.ConvertAll(c => string.Format(
"{0} - {1}", c.help_text, c.long_help));
232 helpText.AddRange(ourHelpText);
242 private List<string> CollectModulesHelp(Dictionary<string, object> dict)
244 lock (m_modulesCommands)
246 List<string> helpText =
new List<string>(m_modulesCommands.Keys);
280 public void AddCommand(
string module,
bool shared,
string command,
283 AddCommand(module, shared, command, help, longhelp, String.Empty, fn);
295 public void AddCommand(
string module,
bool shared,
string command,
296 string help,
string longhelp,
string descriptivehelp,
299 string[] parts = Parser.Parse(command);
301 Dictionary<string, Object> current = tree;
303 foreach (
string part
in parts)
305 if (current.ContainsKey(part))
307 if (current[part] is Dictionary<string, Object>)
308 current = (Dictionary<string, Object>)current[part];
314 current[part] =
new Dictionary<string, Object>();
315 current = (Dictionary<string, Object>)current[part];
321 if (current.ContainsKey(String.Empty))
323 info = (CommandInfo)current[String.Empty];
324 if (!info.shared && !info.fn.Contains(fn))
330 info =
new CommandInfo();
331 info.module = module;
332 info.shared = shared;
333 info.help_text = help;
334 info.long_help = longhelp;
335 info.descriptive_help = descriptivehelp;
336 info.fn =
new List<CommandDelegate>();
338 current[String.Empty] = info;
341 lock (m_modulesCommands)
343 List<CommandInfo> commands;
344 if (m_modulesCommands.ContainsKey(module))
346 commands = m_modulesCommands[module];
350 commands =
new List<CommandInfo>();
351 m_modulesCommands[module] = commands;
361 Dictionary<string, object> current = tree;
363 int remaining = cmd.Length;
365 foreach (
string s
in cmd)
369 List<string> found =
new List<string>();
371 foreach (
string opt
in current.Keys)
373 if (remaining > 0 && opt == s)
379 if (opt.StartsWith(s))
385 if (found.Count == 1 && (remaining != 0 || term))
387 current = (Dictionary<string, object>)current[found[0]];
389 else if (found.Count > 0)
391 return found.ToArray();
400 if (current.Count > 1)
402 List<string> choices =
new List<string>();
405 foreach (
string s
in current.Keys)
407 if (s == String.Empty)
409 CommandInfo ci = (CommandInfo)current[String.Empty];
410 if (ci.fn.Count != 0)
418 return choices.ToArray();
421 if (current.ContainsKey(String.Empty))
422 return new string[] {
"Command help: "+((CommandInfo)current[String.Empty]).help_text};
424 return new string[] {
new List<string>(current.Keys)[0] };
427 private CommandInfo ResolveCommand(
string[] cmd, out
string[] result)
432 Dictionary<string, object> current = tree;
434 foreach (
string s
in cmd)
438 List<string> found =
new List<string>();
440 foreach (
string opt
in current.Keys)
448 if (opt.StartsWith(s))
454 if (found.Count == 1)
456 result[index] = found[0];
457 current = (Dictionary<string, object>)current[found[0]];
459 else if (found.Count > 0)
469 if (current.ContainsKey(String.Empty))
470 return (CommandInfo)current[String.Empty];
478 return ResolveCommand(
Parser.
Parse(command), out result) != null;
484 CommandInfo ci = ResolveCommand(cmd, out result);
487 return new string[0];
489 if (ci.fn.Count == 0)
490 return new string[0];
495 fn(ci.module, result);
497 return new string[0];
503 public XmlElement
GetXml(XmlDocument doc)
505 CommandInfo help = (CommandInfo)((Dictionary<string, object>)tree[
"help"])[String.Empty];
506 ((Dictionary<string, object>)tree[
"help"]).Remove(
string.Empty);
507 if (((Dictionary<string, object>)tree[
"help"]).Count == 0)
510 CommandInfo quit = (CommandInfo)((Dictionary<string, object>)tree[
"quit"])[String.Empty];
511 ((Dictionary<string, object>)tree[
"quit"]).Remove(
string.Empty);
512 if (((Dictionary<string, object>)tree[
"quit"]).Count == 0)
515 XmlElement root = doc.CreateElement(
"",
"HelpTree",
"");
517 ProcessTreeLevel(tree, root, doc);
519 if (!tree.ContainsKey(
"help"))
520 tree[
"help"] = (
object)
new Dictionary<string, object>();
521 ((Dictionary<string, object>)tree[
"help"])[String.Empty] = help;
523 if (!tree.ContainsKey(
"quit"))
524 tree[
"quit"] = (
object)
new Dictionary<string, object>();
525 ((Dictionary<string, object>)tree[
"quit"])[String.Empty] = quit;
530 private void ProcessTreeLevel(Dictionary<string, object> level, XmlElement xml, XmlDocument doc)
532 foreach (KeyValuePair<string, object> kvp
in level)
534 if (kvp.Value is Dictionary<string, Object>)
536 XmlElement next = doc.CreateElement(
"",
"Level",
"");
537 next.SetAttribute(
"Name", kvp.Key);
539 xml.AppendChild(next);
541 ProcessTreeLevel((Dictionary<string, object>)kvp.Value, next, doc);
545 CommandInfo c = (CommandInfo)kvp.Value;
547 XmlElement cmd = doc.CreateElement(
"",
"Command",
"");
551 e = doc.CreateElement(
"",
"Module",
"");
553 e.AppendChild(doc.CreateTextNode(c.module));
555 e = doc.CreateElement(
"",
"Shared",
"");
557 e.AppendChild(doc.CreateTextNode(c.shared.ToString()));
559 e = doc.CreateElement(
"",
"HelpText",
"");
561 e.AppendChild(doc.CreateTextNode(c.help_text));
563 e = doc.CreateElement(
"",
"LongHelp",
"");
565 e.AppendChild(doc.CreateTextNode(c.long_help));
567 e = doc.CreateElement(
"",
"Description",
"");
569 e.AppendChild(doc.CreateTextNode(c.descriptive_help));
571 xml.AppendChild(cmd);
578 CommandInfo help = (CommandInfo)((Dictionary<string, object>)tree[
"help"])[String.Empty];
579 ((Dictionary<string, object>)tree[
"help"]).Remove(
string.Empty);
580 if (((Dictionary<string, object>)tree[
"help"]).Count == 0)
583 CommandInfo quit = (CommandInfo)((Dictionary<string, object>)tree[
"quit"])[String.Empty];
584 ((Dictionary<string, object>)tree[
"quit"]).Remove(
string.Empty);
585 if (((Dictionary<string, object>)tree[
"quit"]).Count == 0)
590 ReadTreeLevel(tree, root, fn);
592 if (!tree.ContainsKey(
"help"))
593 tree[
"help"] = (
object)
new Dictionary<string, object>();
594 ((Dictionary<string, object>)tree[
"help"])[String.Empty] = help;
596 if (!tree.ContainsKey(
"quit"))
597 tree[
"quit"] = (
object)
new Dictionary<string, object>();
598 ((Dictionary<string, object>)tree[
"quit"])[String.Empty] = quit;
601 private void ReadTreeLevel(Dictionary<string, object> level, XmlNode node,
CommandDelegate fn)
603 Dictionary<string, object> next;
606 XmlNodeList nodeL = node.ChildNodes;
610 foreach (XmlNode part
in nodeL)
615 name = ((XmlElement)part).GetAttribute(
"Name");
616 next =
new Dictionary<string, object>();
618 ReadTreeLevel(next, part, fn);
621 cmdL = part.ChildNodes;
622 c =
new CommandInfo();
623 foreach (XmlNode cmdPart
in cmdL)
625 switch (cmdPart.Name)
628 c.module = cmdPart.InnerText;
631 c.shared = Convert.ToBoolean(cmdPart.InnerText);
634 c.help_text = cmdPart.InnerText;
637 c.long_help = cmdPart.InnerText;
640 c.descriptive_help = cmdPart.InnerText;
644 c.fn =
new List<CommandDelegate>();
646 level[String.Empty] = c;
658 private static Regex optionRegex =
new Regex(
"^--[a-zA-Z0-9-]+=$");
660 public static string[]
Parse(
string text)
662 List<string> result =
new List<string>();
666 string[] unquoted = text.Split(
new char[] {
'"'});
668 for (index = 0 ; index < unquoted.Length ; index++)
672 string[] words = unquoted[index].Split(
new char[] {
' '});
675 foreach (
string w
in words)
677 if (w != String.Empty)
679 if (optionRegex.Match(w) == Match.Empty)
690 if (index < (unquoted.Length - 1))
693 string optionText = result[result.Count - 1];
694 result.RemoveAt(result.Count - 1);
697 optionText +=
"\"" + unquoted[index + 1] +
"\"";
700 result.Add(optionText);
709 result.Add(unquoted[index]);
713 return result.ToArray();
733 "Help",
false,
"help",
"help [<item>]",
734 "Display help on a particular command or on a list of commands in a category", Help);
737 private void Help(
string module,
string[] cmd)
739 List<string> help = Commands.GetHelp(cmd);
741 foreach (
string s
in help)
748 if (onOutput != null)
757 string line = ReadLine(DefaultPrompt +
"# ",
true,
true);
759 if (line != String.Empty)
760 Output(
"Invalid command");
765 string[] parts = Parser.Parse(cmd);
766 Commands.Resolve(parts);
769 public override string ReadLine(
string p,
bool isCommand,
bool e)
771 System.Console.Write(
"{0}", p);
772 string cmdinput = System.Console.ReadLine();
776 string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
782 for (i=0 ; i < cmd.Length ; i++)
784 if (cmd[i].Contains(
" "))
785 cmd[i] =
"\"" + cmd[i] +
"\"";
static string[] Parse(string text)
void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn)
Add a command to those which can be invoked from the console.
delegate void CommandDelegate(string module, string[] cmd)
void AddCommand(string module, bool shared, string command, string help, string longhelp, string descriptivehelp, CommandDelegate fn)
Add a command to those which can be invoked from the console.
string[] Resolve(string[] cmd)
delegate void OnOutputDelegate(string message)
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
string[] FindNextOption(string[] cmd, bool term)
bool HasCommand(string command)
Has the given command already been registered?
override string ReadLine(string p, bool isCommand, bool e)
CommandConsole(string defaultPrompt)
void Prompt()
Display a command prompt on the console and wait for user input
Interactive OpenSim region server
void FireOnOutput(string text)
void FromXml(XmlElement root, CommandDelegate fn)
XmlElement GetXml(XmlDocument doc)
List< string > GetHelp(string[] cmd)
Get help for the given help string
void RunCommand(string cmd)
A console that processes commands internally
OnOutputDelegate OnOutput