opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 OpenSimulator 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  
28 using System;
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.Reflection;
32 using System.Text;
33 using System.Text.RegularExpressions;
34 using System.Threading;
35 using log4net;
36  
37 namespace OpenSim.Framework.Console
38 {
39 /// <summary>
40 /// A console that uses cursor control and color
41 /// </summary>
42 public class LocalConsole : CommandConsole
43 {
44 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45  
46 // private readonly object m_syncRoot = new object();
47 private const string LOGLEVEL_NONE = "(none)";
48  
49 private int m_cursorYPosition = -1;
50 private int m_cursorXPosition = 0;
51 private StringBuilder m_commandLine = new StringBuilder();
52 private bool m_echo = true;
53 private List<string> m_history = new List<string>();
54  
55 private static readonly ConsoleColor[] Colors = {
56 // the dark colors don't seem to be visible on some black background terminals like putty :(
57 //ConsoleColor.DarkBlue,
58 //ConsoleColor.DarkGreen,
59 //ConsoleColor.DarkCyan,
60 //ConsoleColor.DarkMagenta,
61 //ConsoleColor.DarkYellow,
62 ConsoleColor.Gray,
63 //ConsoleColor.DarkGray,
64 ConsoleColor.Blue,
65 ConsoleColor.Green,
66 ConsoleColor.Cyan,
67 ConsoleColor.Magenta,
68 ConsoleColor.Yellow
69 };
70  
71 private static ConsoleColor DeriveColor(string input)
72 {
73 // it is important to do Abs, hash values can be negative
74 return Colors[(Math.Abs(input.ToUpper().GetHashCode()) % Colors.Length)];
75 }
76  
77 public LocalConsole(string defaultPrompt) : base(defaultPrompt)
78 {
79 }
80  
81 private void AddToHistory(string text)
82 {
83 while (m_history.Count >= 100)
84 m_history.RemoveAt(0);
85  
86 m_history.Add(text);
87 }
88  
89 /// <summary>
90 /// Set the cursor row.
91 /// </summary>
92 ///
93 /// <param name="top">
94 /// Row to set. If this is below 0, then the row is set to 0. If it is equal to the buffer height or greater
95 /// then it is set to one less than the height.
96 /// </param>
97 /// <returns>
98 /// The new cursor row.
99 /// </returns>
100 private int SetCursorTop(int top)
101 {
102 // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try
103 // to set a cursor row position with a currently invalid column, mono will throw an exception.
104 // Therefore, we need to make sure that the column position is valid first.
105 int left = System.Console.CursorLeft;
106  
107 if (left < 0)
108 {
109 System.Console.CursorLeft = 0;
110 }
111 else
112 {
113 int bufferWidth = System.Console.BufferWidth;
114  
115 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
116 if (bufferWidth > 0 && left >= bufferWidth)
117 System.Console.CursorLeft = bufferWidth - 1;
118 }
119  
120 if (top < 0)
121 {
122 top = 0;
123 }
124 else
125 {
126 int bufferHeight = System.Console.BufferHeight;
127  
128 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
129 if (bufferHeight > 0 && top >= bufferHeight)
130 top = bufferHeight - 1;
131 }
132  
133 System.Console.CursorTop = top;
134  
135 return top;
136 }
137  
138 /// <summary>
139 /// Set the cursor column.
140 /// </summary>
141 ///
142 /// <param name="left">
143 /// Column to set. If this is below 0, then the column is set to 0. If it is equal to the buffer width or greater
144 /// then it is set to one less than the width.
145 /// </param>
146 /// <returns>
147 /// The new cursor column.
148 /// </returns>
149 private int SetCursorLeft(int left)
150 {
151 // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try
152 // to set a cursor column position with a currently invalid row, mono will throw an exception.
153 // Therefore, we need to make sure that the row position is valid first.
154 int top = System.Console.CursorTop;
155  
156 if (top < 0)
157 {
158 System.Console.CursorTop = 0;
159 }
160 else
161 {
162 int bufferHeight = System.Console.BufferHeight;
163 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
164 if (bufferHeight > 0 && top >= bufferHeight)
165 System.Console.CursorTop = bufferHeight - 1;
166 }
167  
168 if (left < 0)
169 {
170 left = 0;
171 }
172 else
173 {
174 int bufferWidth = System.Console.BufferWidth;
175  
176 // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657)
177 if (bufferWidth > 0 && left >= bufferWidth)
178 left = bufferWidth - 1;
179 }
180  
181 System.Console.CursorLeft = left;
182  
183 return left;
184 }
185  
186 private void Show()
187 {
188 lock (m_commandLine)
189 {
190 if (m_cursorYPosition == -1 || System.Console.BufferWidth == 0)
191 return;
192  
193 int xc = prompt.Length + m_cursorXPosition;
194 int new_x = xc % System.Console.BufferWidth;
195 int new_y = m_cursorYPosition + xc / System.Console.BufferWidth;
196 int end_y = m_cursorYPosition + (m_commandLine.Length + prompt.Length) / System.Console.BufferWidth;
197  
198 if (end_y >= System.Console.BufferHeight) // wrap
199 {
200 m_cursorYPosition--;
201 new_y--;
202 SetCursorLeft(0);
203 SetCursorTop(System.Console.BufferHeight - 1);
204 System.Console.WriteLine(" ");
205 }
206  
207 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
208 SetCursorLeft(0);
209  
210 if (m_echo)
211 System.Console.Write("{0}{1}", prompt, m_commandLine);
212 else
213 System.Console.Write("{0}", prompt);
214  
215 SetCursorTop(new_y);
216 SetCursorLeft(new_x);
217 }
218 }
219  
220 public override void LockOutput()
221 {
222 Monitor.Enter(m_commandLine);
223 try
224 {
225 if (m_cursorYPosition != -1)
226 {
227 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
228 System.Console.CursorLeft = 0;
229  
230 int count = m_commandLine.Length + prompt.Length;
231  
232 while (count-- > 0)
233 System.Console.Write(" ");
234  
235 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
236 SetCursorLeft(0);
237 }
238 }
239 catch (Exception)
240 {
241 }
242 }
243  
244 public override void UnlockOutput()
245 {
246 if (m_cursorYPosition != -1)
247 {
248 m_cursorYPosition = System.Console.CursorTop;
249 Show();
250 }
251 Monitor.Exit(m_commandLine);
252 }
253  
254 private void WriteColorText(ConsoleColor color, string sender)
255 {
256 try
257 {
258 lock (this)
259 {
260 try
261 {
262 System.Console.ForegroundColor = color;
263 System.Console.Write(sender);
264 System.Console.ResetColor();
265 }
266 catch (ArgumentNullException)
267 {
268 // Some older systems dont support coloured text.
269 System.Console.WriteLine(sender);
270 }
271 }
272 }
273 catch (ObjectDisposedException)
274 {
275 }
276 }
277  
278 private void WriteLocalText(string text, string level)
279 {
280 string outText = text;
281  
282 if (level != LOGLEVEL_NONE)
283 {
284 string regex = @"^(?<Front>.*?)\[(?<Category>[^\]]+)\]:?(?<End>.*)";
285  
286 Regex RE = new Regex(regex, RegexOptions.Multiline);
287 MatchCollection matches = RE.Matches(text);
288  
289 if (matches.Count == 1)
290 {
291 outText = matches[0].Groups["End"].Value;
292 System.Console.Write(matches[0].Groups["Front"].Value);
293  
294 System.Console.Write("[");
295 WriteColorText(DeriveColor(matches[0].Groups["Category"].Value),
296 matches[0].Groups["Category"].Value);
297 System.Console.Write("]:");
298 }
299 else
300 {
301 outText = outText.Trim();
302 }
303 }
304  
305 if (level == "error")
306 WriteColorText(ConsoleColor.Red, outText);
307 else if (level == "warn")
308 WriteColorText(ConsoleColor.Yellow, outText);
309 else
310 System.Console.Write(outText);
311  
312 System.Console.WriteLine();
313 }
314  
315 public override void Output(string text)
316 {
317 Output(text, LOGLEVEL_NONE);
318 }
319  
320 public override void Output(string text, string level)
321 {
322 FireOnOutput(text);
323  
324 lock (m_commandLine)
325 {
326 if (m_cursorYPosition == -1)
327 {
328 WriteLocalText(text, level);
329  
330 return;
331 }
332  
333 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
334 SetCursorLeft(0);
335  
336 int count = m_commandLine.Length + prompt.Length;
337  
338 while (count-- > 0)
339 System.Console.Write(" ");
340  
341 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
342 SetCursorLeft(0);
343  
344 WriteLocalText(text, level);
345  
346 m_cursorYPosition = System.Console.CursorTop;
347  
348 Show();
349 }
350 }
351  
352 private bool ContextHelp()
353 {
354 string[] words = Parser.Parse(m_commandLine.ToString());
355  
356 bool trailingSpace = m_commandLine.ToString().EndsWith(" ");
357  
358 // Allow ? through while typing a URI
359 //
360 if (words.Length > 0 && words[words.Length-1].StartsWith("http") && !trailingSpace)
361 return false;
362  
363 string[] opts = Commands.FindNextOption(words, trailingSpace);
364  
365 if (opts[0].StartsWith("Command help:"))
366 Output(opts[0]);
367 else
368 Output(String.Format("Options: {0}", String.Join(" ", opts)));
369  
370 return true;
371 }
372  
373 public override string ReadLine(string p, bool isCommand, bool e)
374 {
375 m_cursorXPosition = 0;
376 prompt = p;
377 m_echo = e;
378 int historyLine = m_history.Count;
379  
380 SetCursorLeft(0); // Needed for mono
381 System.Console.Write(" "); // Needed for mono
382  
383 lock (m_commandLine)
384 {
385 m_cursorYPosition = System.Console.CursorTop;
386 m_commandLine.Remove(0, m_commandLine.Length);
387 }
388  
389 while (true)
390 {
391 Show();
392  
393 ConsoleKeyInfo key = System.Console.ReadKey(true);
394 char enteredChar = key.KeyChar;
395  
396 if (!Char.IsControl(enteredChar))
397 {
398 if (m_cursorXPosition >= 318)
399 continue;
400  
401 if (enteredChar == '?' && isCommand)
402 {
403 if (ContextHelp())
404 continue;
405 }
406  
407 m_commandLine.Insert(m_cursorXPosition, enteredChar);
408 m_cursorXPosition++;
409 }
410 else
411 {
412 switch (key.Key)
413 {
414 case ConsoleKey.Backspace:
415 if (m_cursorXPosition == 0)
416 break;
417 m_commandLine.Remove(m_cursorXPosition-1, 1);
418 m_cursorXPosition--;
419  
420 SetCursorLeft(0);
421 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
422  
423 if (m_echo)
424 System.Console.Write("{0}{1} ", prompt, m_commandLine);
425 else
426 System.Console.Write("{0}", prompt);
427  
428 break;
429 case ConsoleKey.Delete:
430 if (m_cursorXPosition == m_commandLine.Length)
431 break;
432  
433 m_commandLine.Remove(m_cursorXPosition, 1);
434  
435 SetCursorLeft(0);
436 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
437  
438 if (m_echo)
439 System.Console.Write("{0}{1} ", prompt, m_commandLine);
440 else
441 System.Console.Write("{0}", prompt);
442  
443 break;
444 case ConsoleKey.End:
445 m_cursorXPosition = m_commandLine.Length;
446 break;
447 case ConsoleKey.Home:
448 m_cursorXPosition = 0;
449 break;
450 case ConsoleKey.UpArrow:
451 if (historyLine < 1)
452 break;
453 historyLine--;
454 LockOutput();
455 m_commandLine.Remove(0, m_commandLine.Length);
456 m_commandLine.Append(m_history[historyLine]);
457 m_cursorXPosition = m_commandLine.Length;
458 UnlockOutput();
459 break;
460 case ConsoleKey.DownArrow:
461 if (historyLine >= m_history.Count)
462 break;
463 historyLine++;
464 LockOutput();
465 if (historyLine == m_history.Count)
466 {
467 m_commandLine.Remove(0, m_commandLine.Length);
468 }
469 else
470 {
471 m_commandLine.Remove(0, m_commandLine.Length);
472 m_commandLine.Append(m_history[historyLine]);
473 }
474 m_cursorXPosition = m_commandLine.Length;
475 UnlockOutput();
476 break;
477 case ConsoleKey.LeftArrow:
478 if (m_cursorXPosition > 0)
479 m_cursorXPosition--;
480 break;
481 case ConsoleKey.RightArrow:
482 if (m_cursorXPosition < m_commandLine.Length)
483 m_cursorXPosition++;
484 break;
485 case ConsoleKey.Enter:
486 SetCursorLeft(0);
487 m_cursorYPosition = SetCursorTop(m_cursorYPosition);
488  
489 System.Console.WriteLine();
490 //Show();
491  
492 lock (m_commandLine)
493 {
494 m_cursorYPosition = -1;
495 }
496  
497 string commandLine = m_commandLine.ToString();
498  
499 if (isCommand)
500 {
501 string[] cmd = Commands.Resolve(Parser.Parse(commandLine));
502  
503 if (cmd.Length != 0)
504 {
505 int index;
506  
507 for (index=0 ; index < cmd.Length ; index++)
508 {
509 if (cmd[index].Contains(" "))
510 cmd[index] = "\"" + cmd[index] + "\"";
511 }
512 AddToHistory(String.Join(" ", cmd));
513 return String.Empty;
514 }
515 }
516  
517 // If we're not echoing to screen (e.g. a password) then we probably don't want it in history
518 if (m_echo && commandLine != "")
519 AddToHistory(commandLine);
520  
521 return commandLine;
522 default:
523 break;
524 }
525 }
526 }
527 }
528 }
529 }