New test.
[mono.git] / mcs / class / corlib / System / TermInfoDriver.cs
1 //
2 // System.ConsoleDriver
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2005,2006 Novell, Inc (http://www.novell.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 #if NET_2_0
31 using System.Collections;
32 using System.IO;
33 using System.Text;
34 using System.Runtime.InteropServices;
35 namespace System {
36         class TermInfoDriver : IConsoleDriver {
37                 static string [] locations = { "/etc/terminfo", "/usr/share/terminfo", "/usr/lib/terminfo" };
38
39                 TermInfoReader reader;
40                 int cursorLeft;
41                 int cursorTop;
42                 string title = String.Empty;
43                 string titleFormat = String.Empty;
44                 bool cursorVisible = true;
45                 string csrVisible;
46                 string csrInvisible;
47                 string clear;
48                 string bell;
49                 string term;
50                 Stream stdin;
51                 internal byte verase;
52                 byte vsusp;
53                 byte intr;
54
55                 int windowWidth;
56                 int windowHeight;
57                 //int windowTop;
58                 //int windowLeft;
59                 int bufferHeight;
60                 int bufferWidth;
61
62                 byte [] buffer;
63                 int readpos;
64                 int writepos;
65                 string keypadXmit, keypadLocal;
66                 bool controlCAsInput;
67                 bool inited;
68                 bool initKeys;
69                 bool noEcho;
70                 string origPair;
71                 string origColors;
72                 string cursorAddress;
73                 ConsoleColor fgcolor = ConsoleColor.White;
74                 ConsoleColor bgcolor = ConsoleColor.Black;
75                 string setafcolor; // TODO: use setforeground/setbackground if available for better
76                 string setabcolor; // color mapping.
77                 bool noGetPosition;
78                 Hashtable keymap;
79                 ByteMatcher rootmap;
80                 bool home_1_1; // if true, we have to add 1 to x and y when using cursorAddress
81
82                 static string SearchTerminfo (string term)
83                 {
84                         if (term == null || term == String.Empty)
85                                 return null;
86
87                         // Ignore TERMINFO and TERMINFO_DIRS by now
88                         //string terminfo = Environment.GetEnvironmentVariable ("TERMINFO");
89                         //string terminfoDirs = Environment.GetEnvironmentVariable ("TERMINFO_DIRS");
90
91                         foreach (string dir in locations) {
92                                 if (!Directory.Exists (dir))
93                                         continue;
94
95                                 string path = Path.Combine (dir, term.Substring (0, 1));
96                                 if (!Directory.Exists (dir))
97                                         continue;
98
99                                 path = Path.Combine (path, term);
100                                 if (!File.Exists (path))
101                                         continue;
102
103                                 return path;
104                         }
105
106                         return null;
107                 }
108
109                 static void WriteConsole (string str)
110                 {
111                         if (str == null)
112                                 return;
113
114                         ((CStreamWriter) Console.stdout).InternalWriteString (str);
115                 }
116
117                 public TermInfoDriver ()
118                         : this (Environment.GetEnvironmentVariable ("TERM"))
119                 {
120                 }
121
122                 public TermInfoDriver (string term)
123                 {
124                         this.term = term;
125
126                         if (term == "xterm") {
127                                 reader = new TermInfoReader (term, KnownTerminals.xterm);
128                         } else if (term == "linux") {
129                                 reader = new TermInfoReader (term, KnownTerminals.linux);
130                         } else {
131                                 string filename = SearchTerminfo (term);
132                                 if (filename != null)
133                                         reader = new TermInfoReader (term, filename);
134                         }
135
136                         if (reader == null)
137                                 reader = new TermInfoReader (term, KnownTerminals.ansi);
138                 }
139
140                 public bool Initialized {
141                         get { return inited; }
142                 }
143                 
144                 public void Init ()
145                 {
146                         if (inited)
147                                 return;
148                         
149                         /* This should not happen any more, since it is checked for in Console */
150                         if (!ConsoleDriver.IsConsole)
151                                 throw new IOException ("Not a tty.");
152
153                         inited = true;
154                         ConsoleDriver.SetEcho (false);
155                         string endString = null;
156                         keypadXmit = reader.Get (TermInfoStrings.KeypadXmit);
157                         keypadLocal = reader.Get (TermInfoStrings.KeypadLocal);
158                         if (keypadXmit != null) {
159                                 WriteConsole (keypadXmit); // Needed to get the arrows working
160                                 if (keypadLocal != null)
161                                         endString += keypadLocal;
162                         }
163
164                         origPair = reader.Get (TermInfoStrings.OrigPair);
165                         origColors = reader.Get (TermInfoStrings.OrigColors);
166                         setafcolor = MangleParameters (reader.Get (TermInfoStrings.SetAForeground));
167                         setabcolor = MangleParameters (reader.Get (TermInfoStrings.SetABackground));
168                         string resetColors = (origColors == null) ? origPair : origColors;
169                         if (resetColors != null)
170                                 endString += resetColors;
171
172                         if (!ConsoleDriver.TtySetup (endString, out verase, out vsusp, out intr))
173                                 throw new IOException ("Error initializing terminal.");
174
175                         stdin = Console.OpenStandardInput (0);
176                         clear = reader.Get (TermInfoStrings.ClearScreen);
177                         bell = reader.Get (TermInfoStrings.Bell);
178                         if (clear == null) {
179                                 clear = reader.Get (TermInfoStrings.CursorHome);
180                                 clear += reader.Get (TermInfoStrings.ClrEos);
181                         }
182
183                         csrVisible = reader.Get (TermInfoStrings.CursorNormal);
184                         if (csrVisible == null)
185                                 csrVisible = reader.Get (TermInfoStrings.CursorVisible);
186
187                         csrInvisible = reader.Get (TermInfoStrings.CursorInvisible);
188                         if (term == "cygwin" || term == "linux" || term.StartsWith ("xterm") ||
189                                 term == "rxvt" || term == "dtterm") {
190                                 titleFormat = "\x1b]0;{0}\x7"; // icon + window title
191                         } else if (term == "iris-ansi") {
192                                 titleFormat = "\x1bP1.y{0}\x1b\\"; // not tested
193                         } else if (term == "sun-cmd") {
194                                 titleFormat = "\x1b]l{0}\x1b\\"; // not tested
195                         }
196
197                         cursorAddress = reader.Get (TermInfoStrings.CursorAddress);
198                         if (cursorAddress != null) {
199                                 string result = cursorAddress.Replace ("%i", String.Empty);
200                                 home_1_1 = (cursorAddress != result);
201                                 cursorAddress = MangleParameters (result);
202                         }
203
204                         GetCursorPosition ();
205                         if (noGetPosition) {
206                                 WriteConsole (clear);
207                                 cursorLeft = 0;
208                                 cursorTop = 0;
209                         }
210                 }
211
212                 static string MangleParameters (string str)
213                 {
214                         if (str == null)
215                                 return null;
216
217                         str = str.Replace ("{", "{{");
218                         str = str.Replace ("}", "}}");
219                         str = str.Replace ("%p1%d", "{0}");
220                         return str.Replace ("%p2%d", "{1}");
221                 }
222
223                 static int TranslateColor (ConsoleColor desired)
224                 {
225                         switch (desired) {
226                         case ConsoleColor.Black:
227                         case ConsoleColor.DarkGray:
228                                 return 0;
229                         case ConsoleColor.DarkBlue:
230                         case ConsoleColor.Blue:
231                                 return 4;
232                         case ConsoleColor.DarkGreen:
233                         case ConsoleColor.Green:
234                                 return 2;
235                         case ConsoleColor.DarkCyan:
236                         case ConsoleColor.Cyan:
237                                 return 6;
238                         case ConsoleColor.DarkRed:
239                         case ConsoleColor.Red:
240                                 return 1;
241                         case ConsoleColor.DarkMagenta:
242                         case ConsoleColor.Magenta:
243                                 return 5;
244                         case ConsoleColor.DarkYellow:
245                         case ConsoleColor.Yellow:
246                                 return 3;
247                         case ConsoleColor.Gray:
248                         case ConsoleColor.White:
249                                 return 7;
250                         }
251
252                         return 0;
253                 }
254
255                 public bool NotifyWrite (ConsoleKeyInfo key)
256                 {
257                         if (key.Key >= ConsoleKey.A && key.Key <= ConsoleKey.Z) {
258                                 cursorLeft++;
259                                 if (cursorLeft >= WindowWidth) {
260                                         cursorTop++;
261                                         cursorLeft = 0;
262                                         if (cursorTop >= WindowHeight) {
263                                                 cursorTop--;
264                                         }
265                                 }
266                                 return true;
267                         }       
268                         switch (key.Key) {
269                         case ConsoleKey.Backspace:
270                                 if (cursorLeft > 0) {
271                                         cursorLeft--;
272                                         SetCursorPosition (cursorLeft, cursorTop);
273                                         WriteConsole (" ");
274                                         SetCursorPosition (cursorLeft, cursorTop);
275                                 }
276                                 return false;
277                         case ConsoleKey.Tab:
278                                 break;
279                         case ConsoleKey.Clear:
280                                 break;
281                         case ConsoleKey.Enter:
282                                 break;
283                         case ConsoleKey.Pause:
284                                 break;
285                         case ConsoleKey.Escape:
286                                 break;
287                         case ConsoleKey.Spacebar:
288                                 break;
289                         case ConsoleKey.PageUp:
290                                 break;
291                         case ConsoleKey.PageDown:
292                                 break;
293                         case ConsoleKey.End:
294                                 break;
295                         case ConsoleKey.Home:
296                                 break;
297                         case ConsoleKey.LeftArrow:
298                                 break;
299                         case ConsoleKey.UpArrow:
300                                 break;
301                         case ConsoleKey.RightArrow:
302                                 break;
303                         case ConsoleKey.DownArrow:
304                                 break;
305                         case ConsoleKey.Select:
306                                 break;
307                         case ConsoleKey.Print:
308                                 break;
309                         case ConsoleKey.Execute:
310                                 break;
311                         case ConsoleKey.PrintScreen:
312                                 break;
313                         case ConsoleKey.Insert:
314                                 break;
315                         case ConsoleKey.Delete:
316                                 break;
317                         case ConsoleKey.Help:
318                                 break;
319                         case ConsoleKey.D0:
320                                 break;
321                         case ConsoleKey.D1:
322                                 break;
323                         case ConsoleKey.D2:
324                                 break;
325                         case ConsoleKey.D3:
326                                 break;
327                         case ConsoleKey.D4:
328                                 break;
329                         case ConsoleKey.D5:
330                                 break;
331                         case ConsoleKey.D6:
332                                 break;
333                         case ConsoleKey.D7:
334                                 break;
335                         case ConsoleKey.D8:
336                                 break;
337                         case ConsoleKey.D9:
338                                 break;
339                         case ConsoleKey.LeftWindows:
340                                 break;
341                         case ConsoleKey.RightWindows:
342                                 break;
343                         case ConsoleKey.Applications:
344                                 break;
345                         case ConsoleKey.Sleep:
346                                 break;
347                         case ConsoleKey.NumPad0:
348                                 break;
349                         case ConsoleKey.NumPad1:
350                                 break;
351                         case ConsoleKey.NumPad2:
352                                 break;
353                         case ConsoleKey.NumPad3:
354                                 break;
355                         case ConsoleKey.NumPad4:
356                                 break;
357                         case ConsoleKey.NumPad5:
358                                 break;
359                         case ConsoleKey.NumPad6:
360                                 break;
361                         case ConsoleKey.NumPad7:
362                                 break;
363                         case ConsoleKey.NumPad8:
364                                 break;
365                         case ConsoleKey.NumPad9:
366                                 break;
367                         case ConsoleKey.Multiply:
368                                 break;
369                         case ConsoleKey.Add:
370                                 break;
371                         case ConsoleKey.Separator:
372                                 break;
373                         case ConsoleKey.Subtract:
374                                 break;
375                         case ConsoleKey.Decimal:
376                                 break;
377                         case ConsoleKey.Divide:
378                                 break;
379                         case ConsoleKey.F1:
380                                 break;
381                         case ConsoleKey.F2:
382                                 break;
383                         case ConsoleKey.F3:
384                                 break;
385                         case ConsoleKey.F4:
386                                 break;
387                         case ConsoleKey.F5:
388                                 break;
389                         case ConsoleKey.F6:
390                                 break;
391                         case ConsoleKey.F7:
392                                 break;
393                         case ConsoleKey.F8:
394                                 break;
395                         case ConsoleKey.F9:
396                                 break;
397                         case ConsoleKey.F10:
398                                 break;
399                         case ConsoleKey.F11:
400                                 break;
401                         case ConsoleKey.F12:
402                                 break;
403                         case ConsoleKey.F13:
404                                 break;
405                         case ConsoleKey.F14:
406                                 break;
407                         case ConsoleKey.F15:
408                                 break;
409                         case ConsoleKey.F16:
410                                 break;
411                         case ConsoleKey.F17:
412                                 break;
413                         case ConsoleKey.F18:
414                                 break;
415                         case ConsoleKey.F19:
416                                 break;
417                         case ConsoleKey.F20:
418                                 break;
419                         case ConsoleKey.F21:
420                                 break;
421                         case ConsoleKey.F22:
422                                 break;
423                         case ConsoleKey.F23:
424                                 break;
425                         case ConsoleKey.F24:
426                                 break;
427                         case ConsoleKey.BrowserBack:
428                                 break;
429                         case ConsoleKey.BrowserForward:
430                                 break;
431                         case ConsoleKey.BrowserRefresh:
432                                 break;
433                         case ConsoleKey.BrowserStop:
434                                 break;
435                         case ConsoleKey.BrowserSearch:
436                                 break;
437                         case ConsoleKey.BrowserFavorites:
438                                 break;
439                         case ConsoleKey.BrowserHome:
440                                 break;
441                         case ConsoleKey.VolumeMute:
442                                 break;
443                         case ConsoleKey.VolumeDown:
444                                 break;
445                         case ConsoleKey.VolumeUp:
446                                 break;
447                         case ConsoleKey.MediaNext:
448                                 break;
449                         case ConsoleKey.MediaPrevious:
450                                 break;
451                         case ConsoleKey.MediaStop:
452                                 break;
453                         case ConsoleKey.MediaPlay:
454                                 break;
455                         case ConsoleKey.LaunchMail:
456                                 break;
457                         case ConsoleKey.LaunchMediaSelect:
458                                 break;
459                         case ConsoleKey.LaunchApp1:
460                                 break;
461                         case ConsoleKey.LaunchApp2:
462                                 break;
463                         case ConsoleKey.Oem1:
464                                 break;
465                         case ConsoleKey.OemPlus:
466                                 break;
467                         case ConsoleKey.OemComma:
468                                 break;
469                         case ConsoleKey.OemMinus:
470                                 break;
471                         case ConsoleKey.OemPeriod:
472                                 break;
473                         case ConsoleKey.Oem2:
474                                 break;
475                         case ConsoleKey.Oem3:
476                                 break;
477                         case ConsoleKey.Oem4:
478                                 break;
479                         case ConsoleKey.Oem5:
480                                 break;
481                         case ConsoleKey.Oem6:
482                                 break;
483                         case ConsoleKey.Oem7:
484                                 break;
485                         case ConsoleKey.Oem8:
486                                 break;
487                         case ConsoleKey.Oem102:
488                                 break;
489                         case ConsoleKey.Process:
490                                 break;
491                         case ConsoleKey.Packet:
492                                 break;
493                         case ConsoleKey.Attention:
494                                 break;
495                         case ConsoleKey.CrSel:
496                                 break;
497                         case ConsoleKey.ExSel:
498                                 break;
499                         case ConsoleKey.EraseEndOfFile:
500                                 break;
501                         case ConsoleKey.Play:
502                                 break;
503                         case ConsoleKey.Zoom:
504                                 break;
505                         case ConsoleKey.NoName:
506                                 break;
507                         case ConsoleKey.Pa1:
508                                 break;
509                         case ConsoleKey.OemClear:
510                                 break;
511                         default:
512                                 return true;
513                         }
514                         return true;
515                 }
516
517                 public bool NotifyWrite (char val)
518                 {
519                         if (val == verase) {
520                                 string str = reader.Get (TermInfoStrings.EraseChars);
521                                 if (str != null) {
522                                         Console.Error.WriteLine ("{0}", TermInfoReader.Escape (str));
523                                 } else {
524                                         Console.Error.WriteLine ("FUCK");
525                                 }
526                                 return false;
527                         } else {
528                                 //TODO: compute cursor position
529                                 return true;
530                         }
531                 }
532
533                 public ConsoleColor BackgroundColor {
534                         get { return bgcolor; }
535                         set {
536                                 bgcolor = value;
537                                 WriteConsole (String.Format (setabcolor, TranslateColor (value)));
538                         }
539                 }
540
541                 public ConsoleColor ForegroundColor {
542                         get { return fgcolor; }
543                         set {
544                                 fgcolor = value;
545                                 WriteConsole (String.Format (setafcolor, TranslateColor (value)));
546                         }
547                 }
548
549                 // Only used once.
550                 void GetCursorPosition ()
551                 {
552                         int row = 0, col = 0;
553                         bool prevEcho = Echo;
554                         Echo = false;
555                         try {
556                                 WriteConsole ("\x1b[6n");
557                                 if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
558                                         noGetPosition = true;
559                                         return;
560                                 }
561
562                                 int b = stdin.ReadByte ();
563                                 while (b != '\x1b') {
564                                         AddToBuffer (b);
565                                         if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
566                                                 return;
567                                         b = stdin.ReadByte ();
568                                 }
569
570                                 b = stdin.ReadByte ();
571                                 if (b != '[') {
572                                         AddToBuffer ('\x1b');
573                                         AddToBuffer (b);
574                                         return;
575                                 }
576
577                                 b = stdin.ReadByte ();
578                                 if (b != ';') {
579                                         row = b - '0';
580                                         b = stdin.ReadByte ();
581                                         while ((b >= '0') && (b <= '9')) {
582                                                 row = row * 10 + b - '0';
583                                                 b = stdin.ReadByte ();
584                                         }
585                                         // Row/col is 0 based
586                                         row --;
587                                 }
588
589                                 b = stdin.ReadByte ();
590                                 if (b != 'R') {
591                                         col = b - '0';
592                                         b = stdin.ReadByte ();
593                                         while ((b >= '0') && (b <= '9')) {
594                                                 col = col * 10 + b - '0';
595                                                 b = stdin.ReadByte ();
596                                         }
597                                         // Row/col is 0 based
598                                         col --;
599                                 }
600                         } finally {
601                                 Echo = prevEcho;
602                         }
603
604                         cursorLeft = col;
605                         cursorTop = row;
606                 }
607
608                 public int BufferHeight {
609                         get {
610                                 return bufferHeight;
611                         }
612                         set {
613                                 throw new NotSupportedException ();
614                         }
615                 }
616
617                 public int BufferWidth {
618                         get {
619                                 return bufferWidth;
620                         }
621                         set {
622                                 throw new NotSupportedException ();
623                         }
624                 }
625
626                 public bool CapsLock {
627                         get { throw new NotSupportedException (); }
628                 }
629
630                 public int CursorLeft {
631                         get {
632                                 return cursorLeft;
633                         }
634                         set {
635                                 SetCursorPosition (value, CursorTop);
636                                 cursorLeft = value;
637                         }
638                 }
639
640                 public int CursorTop {
641                         get {
642                                 return cursorTop;
643                         }
644                         set {
645                                 SetCursorPosition (CursorLeft, value);
646                                 cursorTop = value;
647                         }
648                 }
649
650                 public bool CursorVisible {
651                         get {
652                                 return cursorVisible;
653                         }
654                         set {
655                                 cursorVisible = value;
656                                 WriteConsole ((value ? csrVisible : csrInvisible));
657                         }
658                 }
659
660                 // we have CursorNormal vs. CursorVisible...
661                 [MonoTODO]
662                 public int CursorSize {
663                         get { return 1; }
664                         set {}
665                 }
666
667                 public bool Echo {
668                         get { return !noEcho; }
669                         set {
670                                 if (value != noEcho)
671                                         return;
672
673                                 noEcho = !value;
674                         }
675                 }
676
677                 public bool KeyAvailable {
678                         get {
679                                 return (writepos > readpos || ConsoleDriver.InternalKeyAvailable (0) > 0);
680                         }
681                 }
682
683                 // We don't know these next 2 values, so return something reasonable
684                 public int LargestWindowHeight {
685                         get { return WindowHeight; }
686                 }
687
688                 public int LargestWindowWidth {
689                         get { return WindowWidth; }
690                 }
691
692                 public bool NumberLock {
693                         get { throw new NotSupportedException (); }
694                 }
695
696                 public string Title {
697                         get { return title; }
698                         
699                         set {
700                                 title = value;
701                                 WriteConsole (String.Format (titleFormat, value));
702                         }
703                 }
704
705                 public bool TreatControlCAsInput {
706                         get { return controlCAsInput; }
707                         set {
708                                 if (controlCAsInput == value)
709                                         return;
710
711                                 ConsoleDriver.SetBreak (value);
712                                 controlCAsInput = value;
713                         }
714                 }
715
716                 // TODO: use this at the beginning and on every SIGWINCH
717                 void GetWindowDimensions ()
718                 {
719                         /* Try the ioctl first */
720                         if (!ConsoleDriver.GetTtySize (MonoIO.ConsoleOutput, out windowWidth, out windowHeight)) {
721                                 windowWidth = reader.Get (TermInfoNumbers.Columns);
722                                 string env = Environment.GetEnvironmentVariable ("COLUMNS");
723                                 if (env != null) {
724                                         try {
725                                                 windowWidth = (int) UInt32.Parse (env);
726                                         } catch {
727                                         }
728                                 }
729
730                                 windowHeight = reader.Get (TermInfoNumbers.Lines);
731                                 env = Environment.GetEnvironmentVariable ("LINES");
732                                 if (env != null) {
733                                         try {
734                                                 windowHeight = (int) UInt32.Parse (env);
735                                         } catch {
736                                         }
737                                 }
738                         }
739
740                         bufferHeight = windowHeight;
741                         bufferWidth = windowWidth;
742                 }
743
744                 public int WindowHeight {
745                         get {
746                                 GetWindowDimensions ();
747                                 return windowHeight;
748                         }
749                         set {
750                                 throw new NotSupportedException ();
751                         }
752                 }
753
754                 public int WindowLeft {
755                         get {
756                                 //GetWindowDimensions ();
757                                 return 0;
758                         }
759                         set {
760                                 throw new NotSupportedException ();
761                         }
762                 }
763
764                 public int WindowTop {
765                         get {
766                                 //GetWindowDimensions ();
767                                 return 0;
768                         }
769                         set {
770                                 throw new NotSupportedException ();
771                         }
772                 }
773
774                 public int WindowWidth {
775                         get {
776                                 GetWindowDimensions ();
777                                 return windowWidth;
778                         }
779                         set {
780                                 throw new NotSupportedException ();
781                         }
782                 }
783
784                 public void Clear ()
785                 {
786                         WriteConsole (clear);
787                 }
788
789                 public void Beep (int frequency, int duration)
790                 {
791                         WriteConsole (bell);
792                 }
793
794                 public void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
795                                         int targetLeft, int targetTop, Char sourceChar,
796                                         ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
797                 {
798                         throw new NotImplementedException ();
799                 }
800
801                 void AddToBuffer (int b)
802                 {
803                         if (buffer == null)
804                                 buffer = new byte [1024];
805
806                         buffer [writepos++] = (byte) b;
807                 }
808
809                 ConsoleKeyInfo CreateKeyInfoFromInt (int n)
810                 {
811                         char c = (char) n;
812                         ConsoleKey key = (ConsoleKey)n;
813                         bool shift = false;
814                         bool ctrl = false;
815                         bool alt = false;
816
817                         if (n == 10) {
818                                 key = ConsoleKey.Enter;
819                         } else if (n == 8 || n == 9 || n == 12 || n == 13 || n == 19) {
820                                 /* Values in ConsoleKey */
821                         } else if (n >= 1 && n <= 26) {
822                                 // For Ctrl-a to Ctrl-z.
823                                 ctrl = true;
824                                 key = ConsoleKey.A + n - 1;
825                         } else if (n == 27) {
826                                 key = ConsoleKey.Escape;
827                         } else if (n >= 'a' && n <= 'z') {
828                                 key = ConsoleKey.A - 'a' + n;
829                         } else if (n >= 'A' && n <= 'Z') {
830                                 shift = true;
831                         } else if (n >= '0' && n <= '9') {
832                         } else {
833                                 key = 0;
834                         }
835
836                         return new ConsoleKeyInfo (c, key, shift, alt, ctrl);
837                 }
838
839                 object GetKeyFromBuffer (bool cooked)
840                 {
841                         if (readpos >= writepos)
842                                 return null;
843
844                         int next = buffer [readpos];
845                         if (!cooked || !rootmap.StartsWith (next)) {
846                                 readpos++;
847                                 return CreateKeyInfoFromInt (next);
848                         }
849
850                         int used;
851                         TermInfoStrings str = rootmap.Match (buffer, readpos, writepos - readpos, out used);
852                         if ((int) str == -1)
853                                 return null;
854
855                         ConsoleKeyInfo key;
856                         if (keymap [str] != null) {
857                                 key = (ConsoleKeyInfo) keymap [str];
858                         } else {
859                                 readpos++;
860                                 return CreateKeyInfoFromInt (next);
861                         }
862
863                         readpos += used;
864                         if (readpos >= writepos)
865                                 readpos = writepos = 0;
866
867                         return key;
868                 }
869
870                 ConsoleKeyInfo ReadKeyInternal ()
871                 {
872                         InitKeys ();
873                         object o = null;
874                         while (o == null) {
875                                 o = GetKeyFromBuffer (true);
876                                 if (o == null) {
877                                         if (ConsoleDriver.InternalKeyAvailable (150) > 0) {
878                                                 do {
879                                                         AddToBuffer (stdin.ReadByte ());
880                                                 } while (ConsoleDriver.InternalKeyAvailable (0) > 0);
881                                         } else {
882                                                 o = GetKeyFromBuffer (false);
883                                                 if (o == null)
884                                                         AddToBuffer (stdin.ReadByte ());
885                                         }
886                                 }
887                         }
888
889                         return (ConsoleKeyInfo) o;
890                 }
891
892                 public ConsoleKeyInfo ReadKey (bool intercept)
893                 {
894                         bool prevEcho = Echo;
895                         if (prevEcho == intercept)
896                                 Echo  = !intercept;
897
898                         ConsoleKeyInfo key = ReadKeyInternal ();
899                         if (prevEcho == intercept)
900                                 Echo = prevEcho;
901
902                         return key;
903                 }
904
905                 public string ReadLine ()
906                 {
907                         bool prevEcho = Echo;
908                         if (prevEcho == false)
909                                 Echo  = true;
910                         StringBuilder builder = new StringBuilder ();
911                         bool exit = false;
912                         CStreamWriter writer = (CStreamWriter) Console.stdout;
913                         do {
914                                 ConsoleKeyInfo key = ReadKeyInternal ();
915                                 char c = key.KeyChar;
916                                 exit = (c == '\n');
917                                 if (!exit)
918                                         builder.Append (c);
919                                 writer.WriteKey (key);
920                         } while (!exit);
921                         if (prevEcho == false)
922                                 Echo = prevEcho;
923                         return builder.ToString ();
924                 }
925
926                 public void ResetColor ()
927                 {
928                         string str = (origPair != null) ? origPair : origColors;
929                         WriteConsole (str);
930                 }
931
932                 public void SetBufferSize (int width, int height)
933                 {
934                         throw new NotImplementedException (String.Empty);
935                 }
936
937                 public void SetCursorPosition (int left, int top)
938                 {
939                         if (bufferWidth == 0)
940                                 GetWindowDimensions ();
941
942                         if (left < 0 || left >= bufferWidth)
943                                 throw new ArgumentOutOfRangeException ("left", "Value must be positive and below the buffer width.");
944
945                         if (top < 0 || top >= bufferHeight)
946                                 throw new ArgumentOutOfRangeException ("top", "Value must be positive and below the buffer height.");
947
948                         // Either CursorAddress or nothing.
949                         // We might want to play with up/down/left/right/home when ca is not available.
950                         if (cursorAddress == null)
951                                 throw new NotSupportedException ("This terminal does not suport setting the cursor position.");
952
953                         int one = (home_1_1 ? 1 : 0);
954                         WriteConsole (String.Format (cursorAddress, top + one, left + one));
955                         cursorLeft = left;
956                         cursorTop = top;
957                 }
958
959                 public void SetWindowPosition (int left, int top)
960                 {
961                         // No need to throw exceptions here.
962                         //throw new NotSupportedException ();
963                 }
964
965                 public void SetWindowSize (int width, int height)
966                 {
967                         // No need to throw exceptions here.
968                         //throw new NotSupportedException ();
969                 }
970
971
972                 void CreateKeyMap ()
973                 {
974                         keymap = new Hashtable ();
975                         keymap [TermInfoStrings.KeyBackspace] = new ConsoleKeyInfo ('\0', ConsoleKey.Backspace, false, false, false);
976                         keymap [TermInfoStrings.KeyClear] = new ConsoleKeyInfo ('\0', ConsoleKey.Clear, false, false, false);
977                         // Delete character...
978                         keymap [TermInfoStrings.KeyDown] = new ConsoleKeyInfo ('\0', ConsoleKey.DownArrow, false, false, false);
979                         keymap [TermInfoStrings.KeyF1] = new ConsoleKeyInfo ('\0', ConsoleKey.F1, false, false, false);
980                         keymap [TermInfoStrings.KeyF10] = new ConsoleKeyInfo ('\0', ConsoleKey.F10, false, false, false);
981                         keymap [TermInfoStrings.KeyF2] = new ConsoleKeyInfo ('\0', ConsoleKey.F2, false, false, false);
982                         keymap [TermInfoStrings.KeyF3] = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false);
983                         keymap [TermInfoStrings.KeyF4] = new ConsoleKeyInfo ('\0', ConsoleKey.F4, false, false, false);
984                         keymap [TermInfoStrings.KeyF5] = new ConsoleKeyInfo ('\0', ConsoleKey.F5, false, false, false);
985                         keymap [TermInfoStrings.KeyF6] = new ConsoleKeyInfo ('\0', ConsoleKey.F6, false, false, false);
986                         keymap [TermInfoStrings.KeyF7] = new ConsoleKeyInfo ('\0', ConsoleKey.F7, false, false, false);
987                         keymap [TermInfoStrings.KeyF8] = new ConsoleKeyInfo ('\0', ConsoleKey.F8, false, false, false);
988                         keymap [TermInfoStrings.KeyF9] = new ConsoleKeyInfo ('\0', ConsoleKey.F9, false, false, false);
989                         keymap [TermInfoStrings.KeyHome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
990
991                         keymap [TermInfoStrings.KeyLeft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, false, false, false);
992                         keymap [TermInfoStrings.KeyLl] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
993                         keymap [TermInfoStrings.KeyNpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
994                         keymap [TermInfoStrings.KeyPpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
995                         keymap [TermInfoStrings.KeyRight] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, false, false, false);
996                         keymap [TermInfoStrings.KeySf] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
997                         keymap [TermInfoStrings.KeySr] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
998                         keymap [TermInfoStrings.KeyUp] = new ConsoleKeyInfo ('\0', ConsoleKey.UpArrow, false, false, false);
999                         keymap [TermInfoStrings.KeyA1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad7, false, false, false);
1000                         keymap [TermInfoStrings.KeyA3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad9, false, false, false);
1001                         keymap [TermInfoStrings.KeyB2] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad5, false, false, false);
1002                         keymap [TermInfoStrings.KeyC1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
1003                         keymap [TermInfoStrings.KeyC3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad3, false, false, false);
1004                         keymap [TermInfoStrings.KeyBtab] = new ConsoleKeyInfo ('\0', ConsoleKey.Tab, true, false, false);
1005                         keymap [TermInfoStrings.KeyBeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
1006                         keymap [TermInfoStrings.KeyCopy] = new ConsoleKeyInfo ('C', ConsoleKey.C, false, true, false);
1007                         keymap [TermInfoStrings.KeyEnd] = new ConsoleKeyInfo ('\0', ConsoleKey.End, false, false, false);
1008                         keymap [TermInfoStrings.KeyEnter] = new ConsoleKeyInfo ('\n', ConsoleKey.Enter, false, false, false);
1009                         keymap [TermInfoStrings.KeyHelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, false, false, false);
1010                         keymap [TermInfoStrings.KeyPrint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, false, false, false);
1011                         keymap [TermInfoStrings.KeyUndo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z , false, true, false);
1012                         keymap [TermInfoStrings.KeySbeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1013                         keymap [TermInfoStrings.KeyScopy] = new ConsoleKeyInfo ('C', ConsoleKey.C , true, true, false);
1014                         keymap [TermInfoStrings.KeySdc] = new ConsoleKeyInfo ('\x9', ConsoleKey.Delete, true, false, false);
1015                         keymap [TermInfoStrings.KeyShelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, true, false, false);
1016                         keymap [TermInfoStrings.KeyShome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1017                         keymap [TermInfoStrings.KeySleft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, true, false, false);
1018                         keymap [TermInfoStrings.KeySprint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, true, false, false);
1019                         keymap [TermInfoStrings.KeySright] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, true, false, false);
1020                         keymap [TermInfoStrings.KeySundo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z, true, false, false);
1021                         keymap [TermInfoStrings.KeyF11] = new ConsoleKeyInfo ('\0', ConsoleKey.F11, false, false, false);
1022                         keymap [TermInfoStrings.KeyF12] = new ConsoleKeyInfo ('\0', ConsoleKey.F12 , false, false, false);
1023                         keymap [TermInfoStrings.KeyF13] = new ConsoleKeyInfo ('\0', ConsoleKey.F13, false, false, false);
1024                         keymap [TermInfoStrings.KeyF14] = new ConsoleKeyInfo ('\0', ConsoleKey.F14, false, false, false);
1025                         keymap [TermInfoStrings.KeyF15] = new ConsoleKeyInfo ('\0', ConsoleKey.F15, false, false, false);
1026                         keymap [TermInfoStrings.KeyF16] = new ConsoleKeyInfo ('\0', ConsoleKey.F16, false, false, false);
1027                         keymap [TermInfoStrings.KeyF17] = new ConsoleKeyInfo ('\0', ConsoleKey.F17, false, false, false);
1028                         keymap [TermInfoStrings.KeyF18] = new ConsoleKeyInfo ('\0', ConsoleKey.F18, false, false, false);
1029                         keymap [TermInfoStrings.KeyF19] = new ConsoleKeyInfo ('\0', ConsoleKey.F19, false, false, false);
1030                         keymap [TermInfoStrings.KeyF20] = new ConsoleKeyInfo ('\0', ConsoleKey.F20, false, false, false);
1031                         keymap [TermInfoStrings.KeyF21] = new ConsoleKeyInfo ('\0', ConsoleKey.F21, false, false, false);
1032                         keymap [TermInfoStrings.KeyF22] = new ConsoleKeyInfo ('\0', ConsoleKey.F22, false, false, false);
1033                         keymap [TermInfoStrings.KeyF23] = new ConsoleKeyInfo ('\0', ConsoleKey.F23, false, false, false);
1034                         keymap [TermInfoStrings.KeyF24] = new ConsoleKeyInfo ('\0', ConsoleKey.F24, false, false, false);
1035                 }
1036
1037                 void InitKeys ()
1038                 {
1039                         if (initKeys)
1040                                 return;
1041
1042                         CreateKeyMap ();
1043                         rootmap = new ByteMatcher ();
1044                         AddStringMapping (TermInfoStrings.KeyBackspace);
1045                         AddStringMapping (TermInfoStrings.KeyClear);
1046                         AddStringMapping (TermInfoStrings.KeyDown);
1047                         AddStringMapping (TermInfoStrings.KeyF1);
1048                         AddStringMapping (TermInfoStrings.KeyF10);
1049                         AddStringMapping (TermInfoStrings.KeyF2);
1050                         AddStringMapping (TermInfoStrings.KeyF3);
1051                         AddStringMapping (TermInfoStrings.KeyF4);
1052                         AddStringMapping (TermInfoStrings.KeyF5);
1053                         AddStringMapping (TermInfoStrings.KeyF6);
1054                         AddStringMapping (TermInfoStrings.KeyF7);
1055                         AddStringMapping (TermInfoStrings.KeyF8);
1056                         AddStringMapping (TermInfoStrings.KeyF9);
1057                         AddStringMapping (TermInfoStrings.KeyHome);
1058                         AddStringMapping (TermInfoStrings.KeyLeft);
1059                         AddStringMapping (TermInfoStrings.KeyLl);
1060                         AddStringMapping (TermInfoStrings.KeyNpage);
1061                         AddStringMapping (TermInfoStrings.KeyPpage);
1062                         AddStringMapping (TermInfoStrings.KeyRight);
1063                         AddStringMapping (TermInfoStrings.KeySf);
1064                         AddStringMapping (TermInfoStrings.KeySr);
1065                         AddStringMapping (TermInfoStrings.KeyUp);
1066                         AddStringMapping (TermInfoStrings.KeyA1);
1067                         AddStringMapping (TermInfoStrings.KeyA3);
1068                         AddStringMapping (TermInfoStrings.KeyB2);
1069                         AddStringMapping (TermInfoStrings.KeyC1);
1070                         AddStringMapping (TermInfoStrings.KeyC3);
1071                         AddStringMapping (TermInfoStrings.KeyBtab);
1072                         AddStringMapping (TermInfoStrings.KeyBeg);
1073                         AddStringMapping (TermInfoStrings.KeyCopy);
1074                         AddStringMapping (TermInfoStrings.KeyEnd);
1075                         AddStringMapping (TermInfoStrings.KeyEnter);
1076                         AddStringMapping (TermInfoStrings.KeyHelp);
1077                         AddStringMapping (TermInfoStrings.KeyPrint);
1078                         AddStringMapping (TermInfoStrings.KeyUndo);
1079                         AddStringMapping (TermInfoStrings.KeySbeg);
1080                         AddStringMapping (TermInfoStrings.KeyScopy);
1081                         AddStringMapping (TermInfoStrings.KeySdc);
1082                         AddStringMapping (TermInfoStrings.KeyShelp);
1083                         AddStringMapping (TermInfoStrings.KeyShome);
1084                         AddStringMapping (TermInfoStrings.KeySleft);
1085                         AddStringMapping (TermInfoStrings.KeySprint);
1086                         AddStringMapping (TermInfoStrings.KeySright);
1087                         AddStringMapping (TermInfoStrings.KeySundo);
1088                         AddStringMapping (TermInfoStrings.KeyF11);
1089                         AddStringMapping (TermInfoStrings.KeyF12);
1090                         AddStringMapping (TermInfoStrings.KeyF13);
1091                         AddStringMapping (TermInfoStrings.KeyF14);
1092                         AddStringMapping (TermInfoStrings.KeyF15);
1093                         AddStringMapping (TermInfoStrings.KeyF16);
1094                         AddStringMapping (TermInfoStrings.KeyF17);
1095                         AddStringMapping (TermInfoStrings.KeyF18);
1096                         AddStringMapping (TermInfoStrings.KeyF19);
1097                         AddStringMapping (TermInfoStrings.KeyF20);
1098                         AddStringMapping (TermInfoStrings.KeyF21);
1099                         AddStringMapping (TermInfoStrings.KeyF22);
1100                         AddStringMapping (TermInfoStrings.KeyF23);
1101                         AddStringMapping (TermInfoStrings.KeyF24);
1102                         rootmap.Sort ();
1103                         initKeys = true;
1104                 }
1105
1106                 void AddStringMapping (TermInfoStrings s)
1107                 {
1108                         byte [] bytes = reader.GetStringBytes (s);
1109                         if (bytes == null)
1110                                 return;
1111
1112                         rootmap.AddMapping (s, bytes);
1113                 }
1114         }
1115
1116         class ByteMatcher {
1117                 Hashtable map = new Hashtable ();
1118                 Hashtable starts = new Hashtable ();
1119
1120                 public void AddMapping (TermInfoStrings key, byte [] val)
1121                 {
1122                         if (val.Length == 0)
1123                                 return;
1124
1125                         map [val] = key;
1126                         starts [(int) val [0]] = true;
1127                 }
1128
1129                 public void Sort ()
1130                 {
1131                 }
1132
1133                 public bool StartsWith (int c)
1134                 {
1135                         return (starts [c] != null);
1136                 }
1137
1138                 public TermInfoStrings Match (byte [] buffer, int offset, int length, out int used)
1139                 {
1140                         foreach (byte [] bytes in map.Keys) {
1141                                 for (int i = 0; i < bytes.Length && i < length; i++) {
1142                                         if (bytes [i] != buffer [offset + i])
1143                                                 break;
1144
1145                                         if (bytes.Length - 1 == i) {
1146                                                 used = bytes.Length;
1147                                                 return (TermInfoStrings) map [bytes];
1148                                         }
1149                                 }
1150                         }
1151
1152                         used = 0;
1153                         return (TermInfoStrings) (-1);
1154                 }
1155         }
1156 }
1157 #endif
1158