ConsoleBase.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Diagnostics;
  30. using System.Reflection;
  31. using System.Text;
  32. using System.Threading;
  33. using log4net;
  34. namespace OpenSim.Framework.Console
  35. {
  36. public delegate void CommandDelegate(string module, string[] cmd);
  37. public class Commands
  38. {
  39. /// <summary>
  40. /// Encapsulates a command that can be invoked from the console
  41. /// </summary>
  42. private class CommandInfo
  43. {
  44. /// <value>
  45. /// The module from which this command comes
  46. /// </value>
  47. public string module;
  48. /// <value>
  49. /// Whether the module is shared
  50. /// </value>
  51. public bool shared;
  52. /// <value>
  53. /// Very short BNF description
  54. /// </value>
  55. public string help_text;
  56. /// <value>
  57. /// Longer one line help text
  58. /// </value>
  59. public string long_help;
  60. /// <value>
  61. /// Full descriptive help for this command
  62. /// </value>
  63. public string descriptive_help;
  64. /// <value>
  65. /// The method to invoke for this command
  66. /// </value>
  67. public List<CommandDelegate> fn;
  68. }
  69. /// <value>
  70. /// Commands organized by keyword in a tree
  71. /// </value>
  72. private Dictionary<string, object> tree =
  73. new Dictionary<string, object>();
  74. /// <summary>
  75. /// Get help for the given help string
  76. /// </summary>
  77. /// <param name="helpParts">Parsed parts of the help string. If empty then general help is returned.</param>
  78. /// <returns></returns>
  79. public List<string> GetHelp(string[] cmd)
  80. {
  81. List<string> help = new List<string>();
  82. List<string> helpParts = new List<string>(cmd);
  83. // Remove initial help keyword
  84. helpParts.RemoveAt(0);
  85. // General help
  86. if (helpParts.Count == 0)
  87. {
  88. help.AddRange(CollectHelp(tree));
  89. help.Sort();
  90. }
  91. else
  92. {
  93. help.AddRange(CollectHelp(helpParts));
  94. }
  95. return help;
  96. }
  97. /// <summary>
  98. /// See if we can find the requested command in order to display longer help
  99. /// </summary>
  100. /// <param name="helpParts"></param>
  101. /// <returns></returns>
  102. private List<string> CollectHelp(List<string> helpParts)
  103. {
  104. string originalHelpRequest = string.Join(" ", helpParts.ToArray());
  105. List<string> help = new List<string>();
  106. Dictionary<string, object> dict = tree;
  107. while (helpParts.Count > 0)
  108. {
  109. string helpPart = helpParts[0];
  110. if (!dict.ContainsKey(helpPart))
  111. break;
  112. //m_log.Debug("Found {0}", helpParts[0]);
  113. if (dict[helpPart] is Dictionary<string, Object>)
  114. dict = (Dictionary<string, object>)dict[helpPart];
  115. helpParts.RemoveAt(0);
  116. }
  117. // There was a command for the given help string
  118. if (dict.ContainsKey(String.Empty))
  119. {
  120. CommandInfo commandInfo = (CommandInfo)dict[String.Empty];
  121. help.Add(commandInfo.help_text);
  122. help.Add(commandInfo.long_help);
  123. help.Add(commandInfo.descriptive_help);
  124. }
  125. else
  126. {
  127. help.Add(string.Format("No help is available for {0}", originalHelpRequest));
  128. }
  129. return help;
  130. }
  131. private List<string> CollectHelp(Dictionary<string, object> dict)
  132. {
  133. List<string> result = new List<string>();
  134. foreach (KeyValuePair<string, object> kvp in dict)
  135. {
  136. if (kvp.Value is Dictionary<string, Object>)
  137. {
  138. result.AddRange(CollectHelp((Dictionary<string, Object>)kvp.Value));
  139. }
  140. else
  141. {
  142. if (((CommandInfo)kvp.Value).long_help != String.Empty)
  143. result.Add(((CommandInfo)kvp.Value).help_text+" - "+
  144. ((CommandInfo)kvp.Value).long_help);
  145. }
  146. }
  147. return result;
  148. }
  149. /// <summary>
  150. /// Add a command to those which can be invoked from the console.
  151. /// </summary>
  152. /// <param name="module"></param>
  153. /// <param name="command"></param>
  154. /// <param name="help"></param>
  155. /// <param name="longhelp"></param>
  156. /// <param name="fn"></param>
  157. public void AddCommand(string module, bool shared, string command,
  158. string help, string longhelp, CommandDelegate fn)
  159. {
  160. AddCommand(module, shared, command, help, longhelp,
  161. String.Empty, fn);
  162. }
  163. /// <summary>
  164. /// Add a command to those which can be invoked from the console.
  165. /// </summary>
  166. /// <param name="module"></param>
  167. /// <param name="command"></param>
  168. /// <param name="help"></param>
  169. /// <param name="longhelp"></param>
  170. /// <param name="descriptivehelp"></param>
  171. /// <param name="fn"></param>
  172. public void AddCommand(string module, bool shared, string command,
  173. string help, string longhelp, string descriptivehelp,
  174. CommandDelegate fn)
  175. {
  176. string[] parts = Parser.Parse(command);
  177. Dictionary<string, Object> current = tree;
  178. foreach (string s in parts)
  179. {
  180. if (current.ContainsKey(s))
  181. {
  182. if (current[s] is Dictionary<string, Object>)
  183. {
  184. current = (Dictionary<string, Object>)current[s];
  185. }
  186. else
  187. return;
  188. }
  189. else
  190. {
  191. current[s] = new Dictionary<string, Object>();
  192. current = (Dictionary<string, Object>)current[s];
  193. }
  194. }
  195. CommandInfo info;
  196. if (current.ContainsKey(String.Empty))
  197. {
  198. info = (CommandInfo)current[String.Empty];
  199. if (!info.shared && !info.fn.Contains(fn))
  200. info.fn.Add(fn);
  201. return;
  202. }
  203. info = new CommandInfo();
  204. info.module = module;
  205. info.shared = shared;
  206. info.help_text = help;
  207. info.long_help = longhelp;
  208. info.descriptive_help = descriptivehelp;
  209. info.fn = new List<CommandDelegate>();
  210. info.fn.Add(fn);
  211. current[String.Empty] = info;
  212. }
  213. public string[] FindNextOption(string[] cmd, bool term)
  214. {
  215. Dictionary<string, object> current = tree;
  216. int remaining = cmd.Length;
  217. foreach (string s in cmd)
  218. {
  219. remaining--;
  220. List<string> found = new List<string>();
  221. foreach (string opt in current.Keys)
  222. {
  223. if (remaining > 0 && opt == s)
  224. {
  225. found.Clear();
  226. found.Add(opt);
  227. break;
  228. }
  229. if (opt.StartsWith(s))
  230. {
  231. found.Add(opt);
  232. }
  233. }
  234. if (found.Count == 1 && (remaining != 0 || term))
  235. {
  236. current = (Dictionary<string, object>)current[found[0]];
  237. }
  238. else if (found.Count > 0)
  239. {
  240. return found.ToArray();
  241. }
  242. else
  243. {
  244. break;
  245. // return new string[] {"<cr>"};
  246. }
  247. }
  248. if (current.Count > 1)
  249. {
  250. List<string> choices = new List<string>();
  251. bool addcr = false;
  252. foreach (string s in current.Keys)
  253. {
  254. if (s == String.Empty)
  255. {
  256. CommandInfo ci = (CommandInfo)current[String.Empty];
  257. if (ci.fn.Count != 0)
  258. addcr = true;
  259. }
  260. else
  261. choices.Add(s);
  262. }
  263. if (addcr)
  264. choices.Add("<cr>");
  265. return choices.ToArray();
  266. }
  267. if (current.ContainsKey(String.Empty))
  268. return new string[] { "Command help: "+((CommandInfo)current[String.Empty]).help_text};
  269. return new string[] { new List<string>(current.Keys)[0] };
  270. }
  271. public string[] Resolve(string[] cmd)
  272. {
  273. string[] result = cmd;
  274. int index = -1;
  275. Dictionary<string, object> current = tree;
  276. foreach (string s in cmd)
  277. {
  278. index++;
  279. List<string> found = new List<string>();
  280. foreach (string opt in current.Keys)
  281. {
  282. if (opt == s)
  283. {
  284. found.Clear();
  285. found.Add(opt);
  286. break;
  287. }
  288. if (opt.StartsWith(s))
  289. {
  290. found.Add(opt);
  291. }
  292. }
  293. if (found.Count == 1)
  294. {
  295. result[index] = found[0];
  296. current = (Dictionary<string, object>)current[found[0]];
  297. }
  298. else if (found.Count > 0)
  299. {
  300. return new string[0];
  301. }
  302. else
  303. {
  304. break;
  305. }
  306. }
  307. if (current.ContainsKey(String.Empty))
  308. {
  309. CommandInfo ci = (CommandInfo)current[String.Empty];
  310. if (ci.fn.Count == 0)
  311. return new string[0];
  312. foreach (CommandDelegate fn in ci.fn)
  313. {
  314. if (fn != null)
  315. fn(ci.module, result);
  316. else
  317. return new string[0];
  318. }
  319. return result;
  320. }
  321. return new string[0];
  322. }
  323. }
  324. public class Parser
  325. {
  326. public static string[] Parse(string text)
  327. {
  328. List<string> result = new List<string>();
  329. int index;
  330. string[] unquoted = text.Split(new char[] {'"'});
  331. for (index = 0 ; index < unquoted.Length ; index++)
  332. {
  333. if (index % 2 == 0)
  334. {
  335. string[] words = unquoted[index].Split(new char[] {' '});
  336. foreach (string w in words)
  337. {
  338. if (w != String.Empty)
  339. result.Add(w);
  340. }
  341. }
  342. else
  343. {
  344. result.Add(unquoted[index]);
  345. }
  346. }
  347. return result.ToArray();
  348. }
  349. }
  350. public class ConsoleBase
  351. {
  352. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  353. private readonly object m_syncRoot = new object();
  354. private int y = -1;
  355. private int cp = 0;
  356. private int h = 1;
  357. private string prompt = "# ";
  358. private StringBuilder cmdline = new StringBuilder();
  359. public Commands Commands = new Commands();
  360. private bool echo = true;
  361. private List<string> history = new List<string>();
  362. private bool gui = false;
  363. public object ConsoleScene = null;
  364. /// <summary>
  365. /// The default prompt text.
  366. /// </summary>
  367. public string DefaultPrompt
  368. {
  369. set { m_defaultPrompt = value + "# "; }
  370. get { return m_defaultPrompt; }
  371. }
  372. protected string m_defaultPrompt;
  373. public ConsoleBase(string defaultPrompt)
  374. {
  375. DefaultPrompt = defaultPrompt;
  376. Commands.AddCommand("console", false, "help", "help [<command>]",
  377. "Get general command list or more detailed help on a specific command", Help);
  378. }
  379. public void SetGuiMode(bool mode)
  380. {
  381. gui = mode;
  382. }
  383. private void AddToHistory(string text)
  384. {
  385. while (history.Count >= 100)
  386. history.RemoveAt(0);
  387. history.Add(text);
  388. }
  389. /// <summary>
  390. /// derive an ansi color from a string, ignoring the darker colors.
  391. /// This is used to help automatically bin component tags with colors
  392. /// in various print functions.
  393. /// </summary>
  394. /// <param name="input">arbitrary string for input</param>
  395. /// <returns>an ansii color</returns>
  396. private static ConsoleColor DeriveColor(string input)
  397. {
  398. int colIdx = (input.ToUpper().GetHashCode() % 6) + 9;
  399. return (ConsoleColor) colIdx;
  400. }
  401. /// <summary>
  402. /// Sends a warning to the current console output
  403. /// </summary>
  404. /// <param name="format">The message to send</param>
  405. /// <param name="args">WriteLine-style message arguments</param>
  406. public void Warn(string format, params object[] args)
  407. {
  408. WriteNewLine(ConsoleColor.Yellow, format, args);
  409. }
  410. /// <summary>
  411. /// Sends a warning to the current console output
  412. /// </summary>
  413. /// <param name="sender">The module that sent this message</param>
  414. /// <param name="format">The message to send</param>
  415. /// <param name="args">WriteLine-style message arguments</param>
  416. public void Warn(string sender, string format, params object[] args)
  417. {
  418. WriteNewLine(DeriveColor(sender), sender, ConsoleColor.Yellow, format, args);
  419. }
  420. /// <summary>
  421. /// Sends a notice to the current console output
  422. /// </summary>
  423. /// <param name="format">The message to send</param>
  424. /// <param name="args">WriteLine-style message arguments</param>
  425. public void Notice(string format, params object[] args)
  426. {
  427. WriteNewLine(ConsoleColor.White, format, args);
  428. }
  429. /// <summary>
  430. /// Sends a notice to the current console output
  431. /// </summary>
  432. /// <param name="sender">The module that sent this message</param>
  433. /// <param name="format">The message to send</param>
  434. /// <param name="args">WriteLine-style message arguments</param>
  435. public void Notice(string sender, string format, params object[] args)
  436. {
  437. WriteNewLine(DeriveColor(sender), sender, ConsoleColor.White, format, args);
  438. }
  439. /// <summary>
  440. /// Sends an error to the current console output
  441. /// </summary>
  442. /// <param name="format">The message to send</param>
  443. /// <param name="args">WriteLine-style message arguments</param>
  444. public void Error(string format, params object[] args)
  445. {
  446. WriteNewLine(ConsoleColor.Red, format, args);
  447. }
  448. /// <summary>
  449. /// Sends an error to the current console output
  450. /// </summary>
  451. /// <param name="sender">The module that sent this message</param>
  452. /// <param name="format">The message to send</param>
  453. /// <param name="args">WriteLine-style message arguments</param>
  454. public void Error(string sender, string format, params object[] args)
  455. {
  456. WriteNewLine(DeriveColor(sender), sender, ConsoleColor.Red, format, args);
  457. }
  458. /// <summary>
  459. /// Sends a status message to the current console output
  460. /// </summary>
  461. /// <param name="format">The message to send</param>
  462. /// <param name="args">WriteLine-style message arguments</param>
  463. public void Status(string format, params object[] args)
  464. {
  465. WriteNewLine(ConsoleColor.Blue, format, args);
  466. }
  467. /// <summary>
  468. /// Sends a status message to the current console output
  469. /// </summary>
  470. /// <param name="sender">The module that sent this message</param>
  471. /// <param name="format">The message to send</param>
  472. /// <param name="args">WriteLine-style message arguments</param>
  473. public void Status(string sender, string format, params object[] args)
  474. {
  475. WriteNewLine(DeriveColor(sender), sender, ConsoleColor.Blue, format, args);
  476. }
  477. [Conditional("DEBUG")]
  478. public void Debug(string format, params object[] args)
  479. {
  480. WriteNewLine(ConsoleColor.Gray, format, args);
  481. }
  482. [Conditional("DEBUG")]
  483. public void Debug(string sender, string format, params object[] args)
  484. {
  485. WriteNewLine(DeriveColor(sender), sender, ConsoleColor.Gray, format, args);
  486. }
  487. private int SetCursorTop(int top)
  488. {
  489. if (top >= 0 && top < System.Console.BufferHeight)
  490. {
  491. System.Console.CursorTop = top;
  492. return top;
  493. }
  494. else
  495. {
  496. return System.Console.CursorTop;
  497. }
  498. }
  499. private int SetCursorLeft(int left)
  500. {
  501. if (left >= 0 && left < System.Console.BufferWidth)
  502. {
  503. System.Console.CursorLeft = left;
  504. return left;
  505. }
  506. else
  507. {
  508. return System.Console.CursorLeft;
  509. }
  510. }
  511. private void WriteNewLine(ConsoleColor senderColor, string sender, ConsoleColor color, string format, params object[] args)
  512. {
  513. lock (cmdline)
  514. {
  515. if (y != -1)
  516. {
  517. y=SetCursorTop(y);
  518. System.Console.CursorLeft = 0;
  519. int count = cmdline.Length;
  520. System.Console.Write(" ");
  521. while (count-- > 0)
  522. System.Console.Write(" ");
  523. y=SetCursorTop(y);
  524. System.Console.CursorLeft = 0;
  525. }
  526. WritePrefixLine(senderColor, sender);
  527. WriteConsoleLine(color, format, args);
  528. if (y != -1)
  529. y = System.Console.CursorTop;
  530. }
  531. }
  532. private void WriteNewLine(ConsoleColor color, string format, params object[] args)
  533. {
  534. lock (cmdline)
  535. {
  536. if (y != -1)
  537. {
  538. y=SetCursorTop(y);
  539. System.Console.CursorLeft = 0;
  540. int count = cmdline.Length;
  541. System.Console.Write(" ");
  542. while (count-- > 0)
  543. System.Console.Write(" ");
  544. y=SetCursorTop(y);
  545. System.Console.CursorLeft = 0;
  546. }
  547. WriteConsoleLine(color, format, args);
  548. if (y != -1)
  549. y = System.Console.CursorTop;
  550. }
  551. }
  552. private void WriteConsoleLine(ConsoleColor color, string format, params object[] args)
  553. {
  554. try
  555. {
  556. lock (m_syncRoot)
  557. {
  558. try
  559. {
  560. if (color != ConsoleColor.White)
  561. System.Console.ForegroundColor = color;
  562. System.Console.WriteLine(format, args);
  563. System.Console.ResetColor();
  564. }
  565. catch (ArgumentNullException)
  566. {
  567. // Some older systems dont support coloured text.
  568. System.Console.WriteLine(format, args);
  569. }
  570. catch (FormatException)
  571. {
  572. System.Console.WriteLine(args);
  573. }
  574. }
  575. }
  576. catch (ObjectDisposedException)
  577. {
  578. }
  579. }
  580. private void WritePrefixLine(ConsoleColor color, string sender)
  581. {
  582. try
  583. {
  584. lock (m_syncRoot)
  585. {
  586. sender = sender.ToUpper();
  587. System.Console.WriteLine("[" + sender + "] ");
  588. System.Console.Write("[");
  589. try
  590. {
  591. System.Console.ForegroundColor = color;
  592. System.Console.Write(sender);
  593. System.Console.ResetColor();
  594. }
  595. catch (ArgumentNullException)
  596. {
  597. // Some older systems dont support coloured text.
  598. System.Console.WriteLine(sender);
  599. }
  600. System.Console.Write("] \t");
  601. }
  602. }
  603. catch (ObjectDisposedException)
  604. {
  605. }
  606. }
  607. private void Help(string module, string[] cmd)
  608. {
  609. List<string> help = Commands.GetHelp(cmd);
  610. foreach (string s in help)
  611. Output(s);
  612. }
  613. private void Show()
  614. {
  615. lock (cmdline)
  616. {
  617. if (y == -1 || System.Console.BufferWidth == 0)
  618. return;
  619. int xc = prompt.Length + cp;
  620. int new_x = xc % System.Console.BufferWidth;
  621. int new_y = y + xc / System.Console.BufferWidth;
  622. int end_y = y + (cmdline.Length + prompt.Length) / System.Console.BufferWidth;
  623. if (end_y / System.Console.BufferWidth >= h)
  624. h++;
  625. if (end_y >= System.Console.BufferHeight) // wrap
  626. {
  627. y--;
  628. new_y--;
  629. System.Console.CursorLeft = 0;
  630. System.Console.CursorTop = System.Console.BufferHeight-1;
  631. System.Console.WriteLine(" ");
  632. }
  633. y=SetCursorTop(y);
  634. System.Console.CursorLeft = 0;
  635. if (echo)
  636. System.Console.Write("{0}{1}", prompt, cmdline);
  637. else
  638. System.Console.Write("{0}", prompt);
  639. SetCursorLeft(new_x);
  640. SetCursorTop(new_y);
  641. }
  642. }
  643. public void LockOutput()
  644. {
  645. Monitor.Enter(cmdline);
  646. try
  647. {
  648. if (y != -1)
  649. {
  650. y = SetCursorTop(y);
  651. System.Console.CursorLeft = 0;
  652. int count = cmdline.Length + prompt.Length;
  653. while (count-- > 0)
  654. System.Console.Write(" ");
  655. y = SetCursorTop(y);
  656. System.Console.CursorLeft = 0;
  657. }
  658. }
  659. catch (Exception)
  660. {
  661. }
  662. }
  663. public void UnlockOutput()
  664. {
  665. if (y != -1)
  666. {
  667. y = System.Console.CursorTop;
  668. Show();
  669. }
  670. Monitor.Exit(cmdline);
  671. }
  672. public void Output(string text)
  673. {
  674. lock (cmdline)
  675. {
  676. if (y == -1)
  677. {
  678. System.Console.WriteLine(text);
  679. return;
  680. }
  681. y = SetCursorTop(y);
  682. System.Console.CursorLeft = 0;
  683. int count = cmdline.Length + prompt.Length;
  684. while (count-- > 0)
  685. System.Console.Write(" ");
  686. y = SetCursorTop(y);
  687. System.Console.CursorLeft = 0;
  688. System.Console.WriteLine(text);
  689. y = System.Console.CursorTop;
  690. Show();
  691. }
  692. }
  693. private bool ContextHelp()
  694. {
  695. string[] words = Parser.Parse(cmdline.ToString());
  696. bool trailingSpace = cmdline.ToString().EndsWith(" ");
  697. // Allow ? through while typing a URI
  698. //
  699. if (words.Length > 0 && words[words.Length-1].StartsWith("http") && !trailingSpace)
  700. return false;
  701. string[] opts = Commands.FindNextOption(words, trailingSpace);
  702. if (opts[0].StartsWith("Command help:"))
  703. Output(opts[0]);
  704. else
  705. Output(String.Format("Options: {0}", String.Join(" ", opts)));
  706. return true;
  707. }
  708. public void Prompt()
  709. {
  710. string line = ReadLine(m_defaultPrompt, true, true);
  711. if (line != String.Empty)
  712. {
  713. m_log.Info("Invalid command");
  714. }
  715. }
  716. public string CmdPrompt(string p)
  717. {
  718. return ReadLine(String.Format("{0}: ", p), false, true);
  719. }
  720. public string CmdPrompt(string p, string def)
  721. {
  722. string ret = ReadLine(String.Format("{0} [{1}]: ", p, def), false, true);
  723. if (ret == String.Empty)
  724. ret = def;
  725. return ret;
  726. }
  727. // Displays a command prompt and returns a default value, user may only enter 1 of 2 options
  728. public string CmdPrompt(string prompt, string defaultresponse, string OptionA, string OptionB)
  729. {
  730. bool itisdone = false;
  731. string temp = CmdPrompt(prompt, defaultresponse);
  732. while (itisdone == false)
  733. {
  734. if ((temp == OptionA) || (temp == OptionB))
  735. {
  736. itisdone = true;
  737. }
  738. else
  739. {
  740. System.Console.WriteLine("Valid options are " + OptionA + " or " + OptionB);
  741. temp = CmdPrompt(prompt, defaultresponse);
  742. }
  743. }
  744. return temp;
  745. }
  746. // Displays a prompt and waits for the user to enter a string, then returns that string
  747. // (Done with no echo and suitable for passwords)
  748. public string PasswdPrompt(string p)
  749. {
  750. return ReadLine(p, false, false);
  751. }
  752. public void RunCommand(string cmd)
  753. {
  754. string[] parts = Parser.Parse(cmd);
  755. Commands.Resolve(parts);
  756. }
  757. public string ReadLine(string p, bool isCommand, bool e)
  758. {
  759. h = 1;
  760. cp = 0;
  761. prompt = p;
  762. echo = e;
  763. int historyLine = history.Count;
  764. if (gui)
  765. {
  766. System.Console.Write("{0}", prompt);
  767. string cmdinput = System.Console.ReadLine();
  768. if (isCommand)
  769. {
  770. string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
  771. if (cmd.Length != 0)
  772. {
  773. int i;
  774. for (i=0 ; i < cmd.Length ; i++)
  775. {
  776. if (cmd[i].Contains(" "))
  777. cmd[i] = "\"" + cmd[i] + "\"";
  778. }
  779. return String.Empty;
  780. }
  781. }
  782. return cmdinput;
  783. }
  784. System.Console.CursorLeft = 0; // Needed for mono
  785. System.Console.Write(" "); // Needed for mono
  786. lock (cmdline)
  787. {
  788. y = System.Console.CursorTop;
  789. cmdline.Remove(0, cmdline.Length);
  790. }
  791. while (true)
  792. {
  793. Show();
  794. ConsoleKeyInfo key = System.Console.ReadKey(true);
  795. char c = key.KeyChar;
  796. if (!Char.IsControl(c))
  797. {
  798. if (cp >= 318)
  799. continue;
  800. if (c == '?' && isCommand)
  801. {
  802. if (ContextHelp())
  803. continue;
  804. }
  805. cmdline.Insert(cp, c);
  806. cp++;
  807. }
  808. else
  809. {
  810. switch (key.Key)
  811. {
  812. case ConsoleKey.Backspace:
  813. if (cp == 0)
  814. break;
  815. cmdline.Remove(cp-1, 1);
  816. cp--;
  817. System.Console.CursorLeft = 0;
  818. y = SetCursorTop(y);
  819. System.Console.Write("{0}{1} ", prompt, cmdline);
  820. break;
  821. case ConsoleKey.End:
  822. cp = cmdline.Length;
  823. break;
  824. case ConsoleKey.Home:
  825. cp = 0;
  826. break;
  827. case ConsoleKey.UpArrow:
  828. if (historyLine < 1)
  829. break;
  830. historyLine--;
  831. LockOutput();
  832. cmdline.Remove(0, cmdline.Length);
  833. cmdline.Append(history[historyLine]);
  834. cp = cmdline.Length;
  835. UnlockOutput();
  836. break;
  837. case ConsoleKey.DownArrow:
  838. if (historyLine >= history.Count)
  839. break;
  840. historyLine++;
  841. LockOutput();
  842. if (historyLine == history.Count)
  843. {
  844. cmdline.Remove(0, cmdline.Length);
  845. }
  846. else
  847. {
  848. cmdline.Remove(0, cmdline.Length);
  849. cmdline.Append(history[historyLine]);
  850. }
  851. cp = cmdline.Length;
  852. UnlockOutput();
  853. break;
  854. case ConsoleKey.LeftArrow:
  855. if (cp > 0)
  856. cp--;
  857. break;
  858. case ConsoleKey.RightArrow:
  859. if (cp < cmdline.Length)
  860. cp++;
  861. break;
  862. case ConsoleKey.Enter:
  863. System.Console.CursorLeft = 0;
  864. y = SetCursorTop(y);
  865. System.Console.WriteLine("{0}{1}", prompt, cmdline);
  866. lock (cmdline)
  867. {
  868. y = -1;
  869. }
  870. if (isCommand)
  871. {
  872. string[] cmd = Commands.Resolve(Parser.Parse(cmdline.ToString()));
  873. if (cmd.Length != 0)
  874. {
  875. int i;
  876. for (i=0 ; i < cmd.Length ; i++)
  877. {
  878. if (cmd[i].Contains(" "))
  879. cmd[i] = "\"" + cmd[i] + "\"";
  880. }
  881. AddToHistory(String.Join(" ", cmd));
  882. return String.Empty;
  883. }
  884. }
  885. AddToHistory(cmdline.ToString());
  886. return cmdline.ToString();
  887. default:
  888. break;
  889. }
  890. }
  891. }
  892. }
  893. }
  894. }