aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Console/CommandConsole.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Console/CommandConsole.cs')
-rw-r--r--OpenSim/Framework/Console/CommandConsole.cs467
1 files changed, 467 insertions, 0 deletions
diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs
new file mode 100644
index 0000000..0c314b9
--- /dev/null
+++ b/OpenSim/Framework/Console/CommandConsole.cs
@@ -0,0 +1,467 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Reflection;
32using System.Text;
33using System.Threading;
34using log4net;
35
36namespace OpenSim.Framework.Console
37{
38 public delegate void CommandDelegate(string module, string[] cmd);
39
40 public class Commands
41 {
42 /// <summary>
43 /// Encapsulates a command that can be invoked from the console
44 /// </summary>
45 private class CommandInfo
46 {
47 /// <value>
48 /// The module from which this command comes
49 /// </value>
50 public string module;
51
52 /// <value>
53 /// Whether the module is shared
54 /// </value>
55 public bool shared;
56
57 /// <value>
58 /// Very short BNF description
59 /// </value>
60 public string help_text;
61
62 /// <value>
63 /// Longer one line help text
64 /// </value>
65 public string long_help;
66
67 /// <value>
68 /// Full descriptive help for this command
69 /// </value>
70 public string descriptive_help;
71
72 /// <value>
73 /// The method to invoke for this command
74 /// </value>
75 public List<CommandDelegate> fn;
76 }
77
78 /// <value>
79 /// Commands organized by keyword in a tree
80 /// </value>
81 private Dictionary<string, object> tree =
82 new Dictionary<string, object>();
83
84 /// <summary>
85 /// Get help for the given help string
86 /// </summary>
87 /// <param name="helpParts">Parsed parts of the help string. If empty then general help is returned.</param>
88 /// <returns></returns>
89 public List<string> GetHelp(string[] cmd)
90 {
91 List<string> help = new List<string>();
92 List<string> helpParts = new List<string>(cmd);
93
94 // Remove initial help keyword
95 helpParts.RemoveAt(0);
96
97 // General help
98 if (helpParts.Count == 0)
99 {
100 help.AddRange(CollectHelp(tree));
101 help.Sort();
102 }
103 else
104 {
105 help.AddRange(CollectHelp(helpParts));
106 }
107
108 return help;
109 }
110
111 /// <summary>
112 /// See if we can find the requested command in order to display longer help
113 /// </summary>
114 /// <param name="helpParts"></param>
115 /// <returns></returns>
116 private List<string> CollectHelp(List<string> helpParts)
117 {
118 string originalHelpRequest = string.Join(" ", helpParts.ToArray());
119 List<string> help = new List<string>();
120
121 Dictionary<string, object> dict = tree;
122 while (helpParts.Count > 0)
123 {
124 string helpPart = helpParts[0];
125
126 if (!dict.ContainsKey(helpPart))
127 break;
128
129 //m_log.Debug("Found {0}", helpParts[0]);
130
131 if (dict[helpPart] is Dictionary<string, Object>)
132 dict = (Dictionary<string, object>)dict[helpPart];
133
134 helpParts.RemoveAt(0);
135 }
136
137 // There was a command for the given help string
138 if (dict.ContainsKey(String.Empty))
139 {
140 CommandInfo commandInfo = (CommandInfo)dict[String.Empty];
141 help.Add(commandInfo.help_text);
142 help.Add(commandInfo.long_help);
143 help.Add(commandInfo.descriptive_help);
144 }
145 else
146 {
147 help.Add(string.Format("No help is available for {0}", originalHelpRequest));
148 }
149
150 return help;
151 }
152
153 private List<string> CollectHelp(Dictionary<string, object> dict)
154 {
155 List<string> result = new List<string>();
156
157 foreach (KeyValuePair<string, object> kvp in dict)
158 {
159 if (kvp.Value is Dictionary<string, Object>)
160 {
161 result.AddRange(CollectHelp((Dictionary<string, Object>)kvp.Value));
162 }
163 else
164 {
165 if (((CommandInfo)kvp.Value).long_help != String.Empty)
166 result.Add(((CommandInfo)kvp.Value).help_text+" - "+
167 ((CommandInfo)kvp.Value).long_help);
168 }
169 }
170 return result;
171 }
172
173 /// <summary>
174 /// Add a command to those which can be invoked from the console.
175 /// </summary>
176 /// <param name="module"></param>
177 /// <param name="command"></param>
178 /// <param name="help"></param>
179 /// <param name="longhelp"></param>
180 /// <param name="fn"></param>
181 public void AddCommand(string module, bool shared, string command,
182 string help, string longhelp, CommandDelegate fn)
183 {
184 AddCommand(module, shared, command, help, longhelp,
185 String.Empty, fn);
186 }
187
188 /// <summary>
189 /// Add a command to those which can be invoked from the console.
190 /// </summary>
191 /// <param name="module"></param>
192 /// <param name="command"></param>
193 /// <param name="help"></param>
194 /// <param name="longhelp"></param>
195 /// <param name="descriptivehelp"></param>
196 /// <param name="fn"></param>
197 public void AddCommand(string module, bool shared, string command,
198 string help, string longhelp, string descriptivehelp,
199 CommandDelegate fn)
200 {
201 string[] parts = Parser.Parse(command);
202
203 Dictionary<string, Object> current = tree;
204
205 foreach (string s in parts)
206 {
207 if (current.ContainsKey(s))
208 {
209 if (current[s] is Dictionary<string, Object>)
210 {
211 current = (Dictionary<string, Object>)current[s];
212 }
213 else
214 return;
215 }
216 else
217 {
218 current[s] = new Dictionary<string, Object>();
219 current = (Dictionary<string, Object>)current[s];
220 }
221 }
222
223 CommandInfo info;
224
225 if (current.ContainsKey(String.Empty))
226 {
227 info = (CommandInfo)current[String.Empty];
228 if (!info.shared && !info.fn.Contains(fn))
229 info.fn.Add(fn);
230
231 return;
232 }
233
234 info = new CommandInfo();
235 info.module = module;
236 info.shared = shared;
237 info.help_text = help;
238 info.long_help = longhelp;
239 info.descriptive_help = descriptivehelp;
240 info.fn = new List<CommandDelegate>();
241 info.fn.Add(fn);
242 current[String.Empty] = info;
243 }
244
245 public string[] FindNextOption(string[] cmd, bool term)
246 {
247 Dictionary<string, object> current = tree;
248
249 int remaining = cmd.Length;
250
251 foreach (string s in cmd)
252 {
253 remaining--;
254
255 List<string> found = new List<string>();
256
257 foreach (string opt in current.Keys)
258 {
259 if (remaining > 0 && opt == s)
260 {
261 found.Clear();
262 found.Add(opt);
263 break;
264 }
265 if (opt.StartsWith(s))
266 {
267 found.Add(opt);
268 }
269 }
270
271 if (found.Count == 1 && (remaining != 0 || term))
272 {
273 current = (Dictionary<string, object>)current[found[0]];
274 }
275 else if (found.Count > 0)
276 {
277 return found.ToArray();
278 }
279 else
280 {
281 break;
282// return new string[] {"<cr>"};
283 }
284 }
285
286 if (current.Count > 1)
287 {
288 List<string> choices = new List<string>();
289
290 bool addcr = false;
291 foreach (string s in current.Keys)
292 {
293 if (s == String.Empty)
294 {
295 CommandInfo ci = (CommandInfo)current[String.Empty];
296 if (ci.fn.Count != 0)
297 addcr = true;
298 }
299 else
300 choices.Add(s);
301 }
302 if (addcr)
303 choices.Add("<cr>");
304 return choices.ToArray();
305 }
306
307 if (current.ContainsKey(String.Empty))
308 return new string[] { "Command help: "+((CommandInfo)current[String.Empty]).help_text};
309
310 return new string[] { new List<string>(current.Keys)[0] };
311 }
312
313 public string[] Resolve(string[] cmd)
314 {
315 string[] result = cmd;
316 int index = -1;
317
318 Dictionary<string, object> current = tree;
319
320 foreach (string s in cmd)
321 {
322 index++;
323
324 List<string> found = new List<string>();
325
326 foreach (string opt in current.Keys)
327 {
328 if (opt == s)
329 {
330 found.Clear();
331 found.Add(opt);
332 break;
333 }
334 if (opt.StartsWith(s))
335 {
336 found.Add(opt);
337 }
338 }
339
340 if (found.Count == 1)
341 {
342 result[index] = found[0];
343 current = (Dictionary<string, object>)current[found[0]];
344 }
345 else if (found.Count > 0)
346 {
347 return new string[0];
348 }
349 else
350 {
351 break;
352 }
353 }
354
355 if (current.ContainsKey(String.Empty))
356 {
357 CommandInfo ci = (CommandInfo)current[String.Empty];
358 if (ci.fn.Count == 0)
359 return new string[0];
360 foreach (CommandDelegate fn in ci.fn)
361 {
362 if (fn != null)
363 fn(ci.module, result);
364 else
365 return new string[0];
366 }
367 return result;
368 }
369
370 return new string[0];
371 }
372 }
373
374 public class Parser
375 {
376 public static string[] Parse(string text)
377 {
378 List<string> result = new List<string>();
379
380 int index;
381
382 string[] unquoted = text.Split(new char[] {'"'});
383
384 for (index = 0 ; index < unquoted.Length ; index++)
385 {
386 if (index % 2 == 0)
387 {
388 string[] words = unquoted[index].Split(new char[] {' '});
389 foreach (string w in words)
390 {
391 if (w != String.Empty)
392 result.Add(w);
393 }
394 }
395 else
396 {
397 result.Add(unquoted[index]);
398 }
399 }
400
401 return result.ToArray();
402 }
403 }
404
405 // A console that processes commands internally
406 //
407 public class CommandConsole : ConsoleBase
408 {
409 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
410
411 public Commands Commands = new Commands();
412
413 public CommandConsole(string defaultPrompt) : base(defaultPrompt)
414 {
415 Commands.AddCommand("console", false, "help", "help [<command>]",
416 "Get general command list or more detailed help on a specific command", Help);
417 }
418
419 private void Help(string module, string[] cmd)
420 {
421 List<string> help = Commands.GetHelp(cmd);
422
423 foreach (string s in help)
424 Output(s);
425 }
426
427 public void Prompt()
428 {
429 string line = ReadLine(m_defaultPrompt, true, true);
430
431 if (line != String.Empty)
432 {
433 m_log.Info("Invalid command");
434 }
435 }
436
437 public void RunCommand(string cmd)
438 {
439 string[] parts = Parser.Parse(cmd);
440 Commands.Resolve(parts);
441 }
442
443 public override string ReadLine(string p, bool isCommand, bool e)
444 {
445 System.Console.Write("{0}", prompt);
446 string cmdinput = System.Console.ReadLine();
447
448 if (isCommand)
449 {
450 string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
451
452 if (cmd.Length != 0)
453 {
454 int i;
455
456 for (i=0 ; i < cmd.Length ; i++)
457 {
458 if (cmd[i].Contains(" "))
459 cmd[i] = "\"" + cmd[i] + "\"";
460 }
461 return String.Empty;
462 }
463 }
464 return cmdinput;
465 }
466 }
467}