Merge pull request #409 from Alkarex/patch-1
[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_1
31
32 //
33 // Defining this writes the output to console.log
34 //#define DEBUG
35
36 using System.Collections;
37 using System.IO;
38 using System.Text;
39 using System.Runtime.InteropServices;
40 namespace System {
41         class TermInfoDriver : IConsoleDriver {
42
43                 // This points to a variable that is updated by unmanage code on window size changes.
44                 unsafe static int *native_terminal_size;
45
46                 // The current size that we believe we have
47                 static int terminal_size;
48                 
49                 //static uint flag = 0xdeadbeef;
50                 readonly static string [] locations = { "/etc/terminfo", "/usr/share/terminfo", "/usr/lib/terminfo" };
51
52                 TermInfoReader reader;
53                 int cursorLeft;
54                 int cursorTop;
55                 string title = String.Empty;
56                 string titleFormat = String.Empty;
57                 bool cursorVisible = true;
58                 string csrVisible;
59                 string csrInvisible;
60                 string clear;
61                 string bell;
62                 string term;
63                 StreamReader stdin;
64                 CStreamWriter stdout;
65
66                 int windowWidth;
67                 int windowHeight;
68                 //int windowTop;
69                 //int windowLeft;
70                 int bufferHeight;
71                 int bufferWidth;
72
73                 char [] buffer;
74                 int readpos;
75                 int writepos;
76                 string keypadXmit, keypadLocal;
77                 bool controlCAsInput;
78                 bool inited;
79                 object initLock = new object ();
80                 bool initKeys;
81                 string origPair;
82                 string origColors;
83                 string cursorAddress;
84                 ConsoleColor fgcolor = ConsoleColor.White;
85                 ConsoleColor bgcolor = ConsoleColor.Black;
86                 bool color16 = false; // terminal supports 16 colors
87                 string setlfgcolor;
88                 string setlbgcolor;
89                 string setfgcolor;
90                 string setbgcolor;
91                 bool noGetPosition;
92                 Hashtable keymap;
93                 ByteMatcher rootmap;
94                 bool home_1_1; // if true, we have to add 1 to x and y when using cursorAddress
95                 int rl_startx = -1, rl_starty = -1;
96                 byte [] control_characters; // Indexed by ControlCharacters.XXXXXX
97 #if DEBUG
98                 StreamWriter logger;
99 #endif
100
101                 static string SearchTerminfo (string term)
102                 {
103                         if (term == null || term == String.Empty)
104                                 return null;
105
106                         // Ignore TERMINFO and TERMINFO_DIRS by now
107                         //string terminfo = Environment.GetEnvironmentVariable ("TERMINFO");
108                         //string terminfoDirs = Environment.GetEnvironmentVariable ("TERMINFO_DIRS");
109
110                         foreach (string dir in locations) {
111                                 if (!Directory.Exists (dir))
112                                         continue;
113
114                                 string path = Path.Combine (dir, term.Substring (0, 1));
115                                 if (!Directory.Exists (dir))
116                                         continue;
117
118                                 path = Path.Combine (path, term);
119                                 if (!File.Exists (path))
120                                         continue;
121
122                                 return path;
123                         }
124
125                         return null;
126                 }
127
128                 void WriteConsole (string str)
129                 {
130                         if (str == null)
131                                 return;
132                         
133                         stdout.InternalWriteString (str);
134                 }
135
136                 public TermInfoDriver ()
137                         : this (Environment.GetEnvironmentVariable ("TERM"))
138                 {
139                 }
140
141                 public TermInfoDriver (string term)
142                 {
143 #if DEBUG
144                         File.Delete ("console.log");
145                         logger = new StreamWriter (File.OpenWrite ("console.log"));
146 #endif
147                         this.term = term;
148
149                         if (term == "xterm") {
150                                 reader = new TermInfoReader (term, KnownTerminals.xterm);
151                                 color16 = true;
152                         } else if (term == "linux") {
153                                 reader = new TermInfoReader (term, KnownTerminals.linux);
154                                 color16 = true;
155                         } else {
156                                 string filename = SearchTerminfo (term);
157                                 if (filename != null)
158                                         reader = new TermInfoReader (term, filename);
159                         }
160
161                         if (reader == null)
162                                 reader = new TermInfoReader (term, KnownTerminals.ansi);
163
164                         if (!(Console.stdout is CStreamWriter)) {
165                                 // Application set its own stdout, we need a reference to the real stdout
166                                 stdout = new CStreamWriter (Console.OpenStandardOutput (0), Console.OutputEncoding);
167                                 ((StreamWriter) stdout).AutoFlush = true;
168                         } else {
169                                 stdout = (CStreamWriter) Console.stdout;
170                         }
171                 }
172
173                 public bool Initialized {
174                         get { return inited; }
175                 }
176
177                 public void Init ()
178                 {
179                         if (inited)
180                                 return;
181
182                         lock (initLock){
183                                 if (inited)
184                                         return;
185                                 inited = true;
186                                 
187                                 /* This should not happen any more, since it is checked for in Console */
188                                 if (!ConsoleDriver.IsConsole)
189                                         throw new IOException ("Not a tty.");
190                                 
191                                 ConsoleDriver.SetEcho (false);
192                                 
193                                 string endString = null;
194                                 keypadXmit = reader.Get (TermInfoStrings.KeypadXmit);
195                                 keypadLocal = reader.Get (TermInfoStrings.KeypadLocal);
196                                 if (keypadXmit != null) {
197                                         WriteConsole (keypadXmit); // Needed to get the arrows working
198                                         if (keypadLocal != null)
199                                                 endString += keypadLocal;
200                                 }
201                                 
202                                 origPair = reader.Get (TermInfoStrings.OrigPair);
203                                 origColors = reader.Get (TermInfoStrings.OrigColors);
204                                 setfgcolor = MangleParameters (reader.Get (TermInfoStrings.SetAForeground));
205                                 setbgcolor = MangleParameters (reader.Get (TermInfoStrings.SetABackground));
206                                 // lighter fg colours are 90 -> 97 rather than 30 -> 37
207                                 setlfgcolor = color16 ? setfgcolor.Replace ("[3", "[9") : setfgcolor;
208                                 // lighter bg colours are 100 -> 107 rather than 40 -> 47
209                                 setlbgcolor = color16 ? setbgcolor.Replace ("[4", "[10") : setbgcolor;
210                                 string resetColors = (origColors == null) ? origPair : origColors;
211                                 if (resetColors != null)
212                                         endString += resetColors;
213                                 
214                                 unsafe {
215                                         if (!ConsoleDriver.TtySetup (keypadXmit, endString, out control_characters, out native_terminal_size)){
216                                                 control_characters = new byte [17];
217                                                 native_terminal_size = null;
218                                                 //throw new IOException ("Error initializing terminal.");
219                                         }
220                                 }
221                                 
222                                 stdin = new StreamReader (Console.OpenStandardInput (0), Console.InputEncoding);
223                                 clear = reader.Get (TermInfoStrings.ClearScreen);
224                                 bell = reader.Get (TermInfoStrings.Bell);
225                                 if (clear == null) {
226                                         clear = reader.Get (TermInfoStrings.CursorHome);
227                                         clear += reader.Get (TermInfoStrings.ClrEos);
228                                 }
229                                 
230                                 csrVisible = reader.Get (TermInfoStrings.CursorNormal);
231                                 if (csrVisible == null)
232                                         csrVisible = reader.Get (TermInfoStrings.CursorVisible);
233                                 
234                                 csrInvisible = reader.Get (TermInfoStrings.CursorInvisible);
235                                 if (term == "cygwin" || term == "linux" || (term != null && term.StartsWith ("xterm")) ||
236                                     term == "rxvt" || term == "dtterm") {
237                                         titleFormat = "\x1b]0;{0}\x7"; // icon + window title
238                                 } else if (term == "iris-ansi") {
239                                         titleFormat = "\x1bP1.y{0}\x1b\\"; // not tested
240                                 } else if (term == "sun-cmd") {
241                                         titleFormat = "\x1b]l{0}\x1b\\"; // not tested
242                                 }
243                                 
244                                 cursorAddress = reader.Get (TermInfoStrings.CursorAddress);
245                                 if (cursorAddress != null) {
246                                         string result = cursorAddress.Replace ("%i", String.Empty);
247                                         home_1_1 = (cursorAddress != result);
248                                         cursorAddress = MangleParameters (result);
249                                 }
250                                 
251                                 GetCursorPosition ();
252 #if DEBUG
253                                 logger.WriteLine ("noGetPosition: {0} left: {1} top: {2}", noGetPosition, cursorLeft, cursorTop);
254                                 logger.Flush ();
255 #endif
256                                 if (noGetPosition) {
257                                         WriteConsole (clear);
258                                         cursorLeft = 0;
259                                         cursorTop = 0;
260                                 }
261                         }
262                 }
263
264                 static string MangleParameters (string str)
265                 {
266                         if (str == null)
267                                 return null;
268
269                         str = str.Replace ("{", "{{");
270                         str = str.Replace ("}", "}}");
271                         str = str.Replace ("%p1%d", "{0}");
272                         return str.Replace ("%p2%d", "{1}");
273                 }
274
275                 static int TranslateColor (ConsoleColor desired, out bool light)
276                 {
277                         switch (desired) {
278                         // Dark colours
279                         case ConsoleColor.Black:
280                                 light = false;
281                                 return 0;
282                         case ConsoleColor.DarkRed:
283                                 light = false;
284                                 return 1;
285                         case ConsoleColor.DarkGreen:
286                                 light = false;
287                                 return 2;
288                         case ConsoleColor.DarkYellow:
289                                 light = false;
290                                 return 3;
291                         case ConsoleColor.DarkBlue:
292                                 light = false;
293                                 return 4;
294                         case ConsoleColor.DarkMagenta:
295                                 light = false;
296                                 return 5;
297                         case ConsoleColor.DarkCyan:
298                                 light = false;
299                                 return 6;
300                         case ConsoleColor.Gray:
301                                 light = false;
302                                 return 7;
303                         // Light colours
304                         case ConsoleColor.DarkGray:
305                                 light = true;
306                                 return 0;
307                         case ConsoleColor.Red:
308                                 light = true;
309                                 return 1;
310                         case ConsoleColor.Green:
311                                 light = true;
312                                 return 2;
313                         case ConsoleColor.Yellow:
314                                 light = true;
315                                 return 3;
316                         case ConsoleColor.Blue:
317                                 light = true;
318                                 return 4;
319                         case ConsoleColor.Magenta:
320                                 light = true;
321                                 return 5;
322                         case ConsoleColor.Cyan:
323                                 light = true;
324                                 return 6;
325                         case ConsoleColor.White:
326                                 light = true;
327                                 return 7;
328                         }
329
330                         light = false;
331
332                         return 0;
333                 }
334
335                 void IncrementX ()
336                 {
337                         cursorLeft++;
338                         if (cursorLeft >= WindowWidth) {
339                                 cursorTop++;
340                                 cursorLeft = 0;
341                                 if (cursorTop >= WindowHeight) {
342                                         // Writing beyond the initial screen
343                                         if (rl_starty != -1) rl_starty--;
344                                         cursorTop--;
345                                 }
346                         }
347                 }
348
349                 // Should never get called unless inited
350                 public void WriteSpecialKey (ConsoleKeyInfo key)
351                 {
352                         switch (key.Key) {
353                         case ConsoleKey.Backspace:
354                                 if (cursorLeft > 0) {
355                                         if (cursorLeft <= rl_startx && cursorTop == rl_starty)
356                                                 break;
357                                         cursorLeft--;
358                                         SetCursorPosition (cursorLeft, cursorTop);
359                                         WriteConsole (" ");
360                                         SetCursorPosition (cursorLeft, cursorTop);
361                                 }
362 #if DEBUG
363                                 logger.WriteLine ("BS left: {0} top: {1}", cursorLeft, cursorTop);
364                                 logger.Flush ();
365 #endif
366                                 break;
367                         case ConsoleKey.Tab:
368                                 int n = 8 - (cursorLeft % 8);
369                                 for (int i = 0; i < n; i++){
370                                         IncrementX ();
371                                 }
372                                 WriteConsole ("\t");
373                                 break;
374                         case ConsoleKey.Clear:
375                                 WriteConsole (clear);
376                                 cursorLeft = 0;
377                                 cursorTop = 0;
378                                 break;
379                         case ConsoleKey.Enter:
380                                 break;
381                         default:
382                                 break;
383                         }
384 #if DEBUG
385                         logger.WriteLine ("left: {0} top: {1}", cursorLeft, cursorTop);
386                         logger.Flush ();
387 #endif
388                 }
389
390                 // Should never get called unless inited
391                 public void WriteSpecialKey (char c)
392                 {
393                         WriteSpecialKey (CreateKeyInfoFromInt (c, false));
394                 }
395
396                 public bool IsSpecialKey (ConsoleKeyInfo key)
397                 {
398                         if (!inited)
399                                 return false;
400
401                         switch (key.Key) {
402                         case ConsoleKey.Backspace:
403                                 return true;
404                         case ConsoleKey.Tab:
405                                 return true;
406                         case ConsoleKey.Clear:
407                                 return true;
408                         case ConsoleKey.Enter:
409                                 cursorLeft = 0;
410                                 cursorTop++;
411                                 if (cursorTop >= WindowHeight) {
412                                         cursorTop--;
413                                         //TODO: scroll up
414                                 }
415                                 return false;
416                         default:
417                                 // CStreamWriter will handle writing this key
418                                 IncrementX ();
419                                 return false;
420                         }
421                 }
422
423                 public bool IsSpecialKey (char c)
424                 {
425                         return IsSpecialKey (CreateKeyInfoFromInt (c, false));
426                 }
427
428                 public ConsoleColor BackgroundColor {
429                         get {
430                                 if (!inited) {
431                                         Init ();
432                                 }
433
434                                 return bgcolor;
435                         }
436                         set {
437                                 if (!inited) {
438                                         Init ();
439                                 }
440
441                                 bgcolor = value;
442
443                                 bool light;
444                                 int colour = TranslateColor (value, out light);
445
446                                 if (light)
447                                         WriteConsole (String.Format (setlbgcolor, colour));
448                                 else
449                                         WriteConsole (String.Format (setbgcolor, colour));
450                         }
451                 }
452
453                 public ConsoleColor ForegroundColor {
454                         get {
455                                 if (!inited) {
456                                         Init ();
457                                 }
458
459                                 return fgcolor;
460                         }
461                         set {
462                                 if (!inited) {
463                                         Init ();
464                                 }
465
466                                 fgcolor = value;
467
468                                 bool light;
469                                 int colour = TranslateColor (value, out light);
470
471                                 if (light)
472                                         WriteConsole (String.Format (setlfgcolor, colour));
473                                 else
474                                         WriteConsole (String.Format (setfgcolor, colour));
475                         }
476                 }
477
478                 void GetCursorPosition ()
479                 {
480                         int row = 0, col = 0;
481                         int b;
482
483                         // First, get any data in the input buffer.  Merely reduces the likelyhood of getting an error
484                         int inqueue = ConsoleDriver.InternalKeyAvailable (0);
485                         while (inqueue-- > 0){
486                                 b = stdin.Read ();
487                                 AddToBuffer (b);
488                         }
489
490                         // Then try to probe for the cursor coordinates
491                         WriteConsole ("\x1b[6n");
492                         if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
493                                 noGetPosition = true;
494                                 return;
495                         }
496
497                         b = stdin.Read ();
498                         while (b != '\x1b') {
499                                 AddToBuffer (b);
500                                 if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
501                                         return;
502                                 b = stdin.Read ();
503                         }
504
505                         b = stdin.Read ();
506                         if (b != '[') {
507                                 AddToBuffer ('\x1b');
508                                 AddToBuffer (b);
509                                 return;
510                         }
511
512                         b = stdin.Read ();
513                         if (b != ';') {
514                                 row = b - '0';
515                                 b = stdin.Read ();
516                                 while ((b >= '0') && (b <= '9')) {
517                                         row = row * 10 + b - '0';
518                                         b = stdin.Read ();
519                                 }
520                                 // Row/col is 0 based
521                                 row --;
522                         }
523
524                         b = stdin.Read ();
525                         if (b != 'R') {
526                                 col = b - '0';
527                                 b = stdin.Read ();
528                                 while ((b >= '0') && (b <= '9')) {
529                                         col = col * 10 + b - '0';
530                                         b = stdin.Read ();
531                                 }
532                                 // Row/col is 0 based
533                                 col --;
534                         }
535
536 #if DEBUG
537                         logger.WriteLine ("GetCursorPosition: {0}, {1}", col, row);
538                         logger.Flush ();
539 #endif
540
541                         cursorLeft = col;
542                         cursorTop = row;
543                 }
544
545                 public int BufferHeight {
546                         get {
547                                 if (!inited) {
548                                         Init ();
549                                 }
550
551                                 CheckWindowDimensions ();
552                                 return bufferHeight;
553                         }
554                         set {
555                                 if (!inited) {
556                                         Init ();
557                                 }
558
559                                 throw new NotSupportedException ();
560                         }
561                 }
562
563                 public int BufferWidth {
564                         get {
565                                 if (!inited) {
566                                         Init ();
567                                 }
568
569                                 CheckWindowDimensions ();
570                                 return bufferWidth;
571                         }
572                         set {
573                                 if (!inited) {
574                                         Init ();
575                                 }
576
577                                 throw new NotSupportedException ();
578                         }
579                 }
580
581                 public bool CapsLock {
582                         get {
583                                 if (!inited) {
584                                         Init ();
585                                 }
586                                 return false;
587                         }
588                 }
589
590                 public int CursorLeft {
591                         get {
592                                 if (!inited) {
593                                         Init ();
594                                 }
595
596                                 return cursorLeft;
597                         }
598                         set {
599                                 if (!inited) {
600                                         Init ();
601                                 }
602
603                                 SetCursorPosition (value, CursorTop);
604                         }
605                 }
606
607                 public int CursorTop {
608                         get {
609                                 if (!inited) {
610                                         Init ();
611                                 }
612
613                                 return cursorTop;
614                         }
615                         set {
616                                 if (!inited) {
617                                         Init ();
618                                 }
619
620                                 SetCursorPosition (CursorLeft, value);
621                         }
622                 }
623
624                 public bool CursorVisible {
625                         get {
626                                 if (!inited) {
627                                         Init ();
628                                 }
629
630                                 return cursorVisible;
631                         }
632                         set {
633                                 if (!inited) {
634                                         Init ();
635                                 }
636
637                                 cursorVisible = value;
638                                 WriteConsole ((value ? csrVisible : csrInvisible));
639                         }
640                 }
641
642                 // we have CursorNormal vs. CursorVisible...
643                 [MonoTODO]
644                 public int CursorSize {
645                         get {
646                                 if (!inited) {
647                                         Init ();
648                                 }
649                                 return 1;
650                         }
651                         set {
652                                 if (!inited) {
653                                         Init ();
654                                 }
655                         }
656
657                 }
658
659                 public bool KeyAvailable {
660                         get {
661                                 if (!inited) {
662                                         Init ();
663                                 }
664
665                                 return (writepos > readpos || ConsoleDriver.InternalKeyAvailable (0) > 0);
666                         }
667                 }
668
669                 // We don't know these next 2 values, so return something reasonable
670                 public int LargestWindowHeight {
671                         get { return WindowHeight; }
672                 }
673
674                 public int LargestWindowWidth {
675                         get { return WindowWidth; }
676                 }
677
678                 public bool NumberLock {
679                         get {
680                                 if (!inited) {
681                                         Init ();
682                                 }
683
684                                 return false;
685                         }
686                 }
687
688                 public string Title {
689                         get {
690                                 if (!inited) {
691                                         Init ();
692                                 }
693                                 return title;
694                         }
695                         
696                         set {
697                                 if (!inited) {
698                                         Init ();
699                                 }
700
701                                 title = value;
702                                 WriteConsole (String.Format (titleFormat, value));
703                         }
704                 }
705
706                 public bool TreatControlCAsInput {
707                         get {
708                                 if (!inited) {
709                                         Init ();
710                                 }
711                                 return controlCAsInput;
712                         }
713                         set {
714                                 if (!inited) {
715                                         Init ();
716                                 }
717
718                                 if (controlCAsInput == value)
719                                         return;
720
721                                 ConsoleDriver.SetBreak (value);
722                                 controlCAsInput = value;
723                         }
724                 }
725
726                 //
727                 // Requries that caller calls Init () if not !inited.
728                 //
729                 unsafe void CheckWindowDimensions ()
730                 {
731                         if (native_terminal_size == null || terminal_size == *native_terminal_size)
732                                 return;
733
734                         if (*native_terminal_size == -1){
735                                 int c = reader.Get (TermInfoNumbers.Columns);
736                                 if (c != 0)
737                                         windowWidth = c;
738                                 
739                                 c = reader.Get (TermInfoNumbers.Lines);
740                                 if (c != 0)
741                                         windowHeight = c;
742                         } else {
743                                 terminal_size = *native_terminal_size;
744                                 windowWidth = terminal_size >> 16;
745                                 windowHeight = terminal_size & 0xffff;
746                         }
747                         bufferHeight = windowHeight;
748                         bufferWidth = windowWidth;
749                 }
750
751                 
752                 public int WindowHeight {
753                         get {
754                                 if (!inited) {
755                                         Init ();
756                                 }
757
758                                 CheckWindowDimensions ();
759                                 return windowHeight;
760                         }
761                         set {
762                                 if (!inited) {
763                                         Init ();
764                                 }
765
766                                 throw new NotSupportedException ();
767                         }
768                 }
769
770                 public int WindowLeft {
771                         get {
772                                 if (!inited) {
773                                         Init ();
774                                 }
775
776                                 //CheckWindowDimensions ();
777                                 return 0;
778                         }
779                         set {
780                                 if (!inited) {
781                                         Init ();
782                                 }
783
784                                 throw new NotSupportedException ();
785                         }
786                 }
787
788                 public int WindowTop {
789                         get {
790                                 if (!inited) {
791                                         Init ();
792                                 }
793
794                                 //CheckWindowDimensions ();
795                                 return 0;
796                         }
797                         set {
798                                 if (!inited) {
799                                         Init ();
800                                 }
801
802                                 throw new NotSupportedException ();
803                         }
804                 }
805
806                 public int WindowWidth {
807                         get {
808                                 if (!inited) {
809                                         Init ();
810                                 }
811
812                                 CheckWindowDimensions ();
813                                 return windowWidth;
814                         }
815                         set {
816                                 if (!inited) {
817                                         Init ();
818                                 }
819
820                                 throw new NotSupportedException ();
821                         }
822                 }
823
824                 public void Clear ()
825                 {
826                         if (!inited) {
827                                 Init ();
828                         }
829
830                         WriteConsole (clear);
831                         cursorLeft = 0;
832                         cursorTop = 0;
833                 }
834
835                 public void Beep (int frequency, int duration)
836                 {
837                         if (!inited) {
838                                 Init ();
839                         }
840
841                         WriteConsole (bell);
842                 }
843
844                 public void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
845                                         int targetLeft, int targetTop, Char sourceChar,
846                                         ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
847                 {
848                         if (!inited) {
849                                 Init ();
850                         }
851
852                         throw new NotImplementedException ();
853                 }
854
855                 void AddToBuffer (int b)
856                 {
857                         if (buffer == null) {
858                                 buffer = new char [1024];
859                         } else if (writepos >= buffer.Length) {
860                                 char [] newbuf = new char [buffer.Length * 2];
861                                 Buffer.BlockCopy (buffer, 0, newbuf, 0, buffer.Length);
862                                 buffer = newbuf;
863                         }
864
865                         buffer [writepos++] = (char) b;
866                 }
867
868                 void AdjustBuffer ()
869                 {
870                         if (readpos >= writepos) {
871                                 readpos = writepos = 0;
872                         }
873                 }
874
875                 ConsoleKeyInfo CreateKeyInfoFromInt (int n, bool alt)
876                 {
877                         char c = (char) n;
878                         ConsoleKey key = (ConsoleKey)n;
879                         bool shift = false;
880                         bool ctrl = false;
881
882                         switch (n){
883                         case 10:
884                                 key = ConsoleKey.Enter;
885                                 break;
886                         case 0x20:
887                                 key = ConsoleKey.Spacebar;
888                                 break;
889                         case 45:
890                                 key = ConsoleKey.Subtract;
891                                 break;
892                         case 43:
893                                 key = ConsoleKey.Add;
894                                 break;
895                         case 47:
896                                 key = ConsoleKey.Divide;
897                                 break;
898                         case 42:
899                                 key = ConsoleKey.Multiply;
900                                 break;
901                         case 8: case 9: case 12: case 13: case 19:
902                                 /* Values in ConsoleKey */
903                                 break;
904                         case 27:
905                                 key = ConsoleKey.Escape;
906                                 break;
907                                 
908                         default:
909                                 if (n >= 1 && n <= 26) {
910                                         // For Ctrl-a to Ctrl-z.
911                                         ctrl = true;
912                                         key = ConsoleKey.A + n - 1;
913                                 } else if (n >= 'a' && n <= 'z') {
914                                         key = ConsoleKey.A - 'a' + n;
915                                 } else if (n >= 'A' && n <= 'Z') {
916                                         shift = true;
917                                 } else if (n >= '0' && n <= '9') {
918                                 } else
919                                         key = 0;
920                                 break;
921                         }
922
923                         return new ConsoleKeyInfo (c, key, shift, alt, ctrl);
924                 }
925
926                 object GetKeyFromBuffer (bool cooked)
927                 {
928                         if (readpos >= writepos)
929                                 return null;
930
931                         int next = buffer [readpos];
932                         if (!cooked || !rootmap.StartsWith (next)) {
933                                 readpos++;
934                                 AdjustBuffer ();
935                                 return CreateKeyInfoFromInt (next, false);
936                         }
937
938                         int used;
939                         TermInfoStrings str = rootmap.Match (buffer, readpos, writepos - readpos, out used);
940                         if ((int) str == -1){
941                                 // Escape sequences: alt keys are sent as ESC-key
942                                 if (buffer [readpos] == 27 && (writepos - readpos) >= 2){
943                                         readpos += 2;
944                                         AdjustBuffer ();
945                                         if (buffer [readpos+1] == 127)
946                                                 return new ConsoleKeyInfo ((char)8, ConsoleKey.Backspace, false, true, false);
947                                         return CreateKeyInfoFromInt (buffer [readpos+1], true);
948                                 } else
949                                         return null;
950                         }
951
952                         ConsoleKeyInfo key;
953                         if (keymap [str] != null) {
954                                 key = (ConsoleKeyInfo) keymap [str];
955                         } else {
956                                 readpos++;
957                                 AdjustBuffer ();
958                                 return CreateKeyInfoFromInt (next, false);
959                         }
960
961                         readpos += used;
962                         AdjustBuffer ();
963                         return key;
964                 }
965
966                 ConsoleKeyInfo ReadKeyInternal (out bool fresh)
967                 {
968                         if (!inited)
969                                 Init ();
970
971                         InitKeys ();
972
973                         object o;
974
975                         if ((o = GetKeyFromBuffer (true)) == null) {
976                                 do {
977                                         if (ConsoleDriver.InternalKeyAvailable (150) > 0) {
978                                                 do {
979                                                         AddToBuffer (stdin.Read ());
980                                                 } while (ConsoleDriver.InternalKeyAvailable (0) > 0);
981                                         } else if (stdin.DataAvailable ()) {
982                                                 do {
983                                                         AddToBuffer (stdin.Read ());
984                                                 } while (stdin.DataAvailable ());
985                                         } else {
986                                                 if ((o = GetKeyFromBuffer (false)) != null)
987                                                         break;
988
989                                                 AddToBuffer (stdin.Read ());
990                                         }
991                                         
992                                         o = GetKeyFromBuffer (true);
993                                 } while (o == null);
994
995                                 // freshly read character
996                                 fresh = true;
997                         } else {
998                                 // this char was pre-buffered (e.g. not fresh)
999                                 fresh = false;
1000                         }
1001
1002                         return (ConsoleKeyInfo) o;
1003                 }
1004
1005 #region Input echoing optimization
1006                 bool InputPending ()
1007                 {
1008                         // check if we've got pending input we can read immediately
1009                         return readpos < writepos || stdin.DataAvailable ();
1010                 }
1011
1012                 char [] echobuf = null;
1013                 int echon = 0;
1014
1015                 // Queues a character to be echo'd back to the console
1016                 void QueueEcho (char c)
1017                 {
1018                         if (echobuf == null)
1019                                 echobuf = new char [1024];
1020
1021                         echobuf[echon++] = c;
1022
1023                         if (echon == echobuf.Length || !InputPending ()) {
1024                                 // blit our echo buffer to the console
1025                                 stdout.InternalWriteChars (echobuf, echon);
1026                                 echon = 0;
1027                         }
1028                 }
1029
1030                 // Queues a key to be echo'd back to the console
1031                 void Echo (ConsoleKeyInfo key)
1032                 {
1033                         if (!IsSpecialKey (key)) {
1034                                 QueueEcho (key.KeyChar);
1035                                 return;
1036                         }
1037
1038                         // flush pending echo's
1039                         EchoFlush ();
1040
1041                         WriteSpecialKey (key);
1042                 }
1043
1044                 // Flush the pending echo queue
1045                 void EchoFlush ()
1046                 {
1047                         if (echon == 0)
1048                                 return;
1049
1050                         // flush our echo buffer to the console
1051                         stdout.InternalWriteChars (echobuf, echon);
1052                         echon = 0;
1053                 }
1054 #endregion
1055
1056                 public int Read ([In, Out] char [] dest, int index, int count)
1057                 {
1058                         bool fresh, echo = false;
1059                         StringBuilder sbuf;
1060                         ConsoleKeyInfo key;
1061                         int BoL = 0;  // Beginning-of-Line marker (can't backspace beyond this)
1062                         object o;
1063                         char c;
1064
1065                         sbuf = new StringBuilder ();
1066
1067                         // consume buffered keys first (do not echo, these have already been echo'd)
1068                         while (true) {
1069                                 if ((o = GetKeyFromBuffer (true)) == null)
1070                                         break;
1071
1072                                 key = (ConsoleKeyInfo) o;
1073                                 c = key.KeyChar;
1074
1075                                 if (key.Key != ConsoleKey.Backspace) {
1076                                         if (key.Key == ConsoleKey.Enter)
1077                                                 BoL = sbuf.Length;
1078
1079                                         sbuf.Append (c);
1080                                 } else if (sbuf.Length > BoL) {
1081                                         sbuf.Length--;
1082                                 }
1083                         }
1084
1085                         // continue reading until Enter is hit
1086                         rl_startx = cursorLeft;
1087                         rl_starty = cursorTop;
1088
1089                         do {
1090                                 key = ReadKeyInternal (out fresh);
1091                                 echo = echo || fresh;
1092                                 c = key.KeyChar;
1093
1094                                 if (key.Key != ConsoleKey.Backspace) {
1095                                         if (key.Key == ConsoleKey.Enter)
1096                                                 BoL = sbuf.Length;
1097
1098                                         sbuf.Append (c);
1099                                 } else if (sbuf.Length > BoL) {
1100                                         sbuf.Length--;
1101                                 } else {
1102                                         continue;
1103                                 }
1104
1105                                 // echo fresh keys back to the console
1106                                 if (echo)
1107                                         Echo (key);
1108                         } while (key.Key != ConsoleKey.Enter);
1109
1110                         EchoFlush ();
1111
1112                         rl_startx = -1;
1113                         rl_starty = -1;
1114
1115                         // copy up to count chars into dest
1116                         int nread = 0;
1117                         while (count > 0 && nread < sbuf.Length) {
1118                                 dest[index + nread] = sbuf[nread];
1119                                 nread++;
1120                                 count--;
1121                         }
1122
1123                         // put the rest back into our key buffer
1124                         for (int i = nread; i < sbuf.Length; i++)
1125                                 AddToBuffer (sbuf[i]);
1126
1127                         return nread;
1128                 }
1129
1130                 public ConsoleKeyInfo ReadKey (bool intercept)
1131                 {
1132                         bool fresh;
1133
1134                         ConsoleKeyInfo key = ReadKeyInternal (out fresh);
1135
1136                         if (!intercept && fresh) {
1137                                 // echo the fresh key back to the console
1138                                 Echo (key);
1139                                 EchoFlush ();
1140                         }
1141
1142                         return key;
1143                 }
1144
1145                 public string ReadLine ()
1146                 {
1147                         if (!inited)
1148                                 Init ();
1149
1150                         // Hack to make Iron Python work (since it goes behind our backs
1151                         // when writing to the console thus preventing us from keeping
1152                         // cursor state normally).
1153                         GetCursorPosition ();
1154
1155                         StringBuilder builder = new StringBuilder ();
1156                         bool fresh, echo = false;
1157                         ConsoleKeyInfo key;
1158                         char c;
1159
1160                         rl_startx = cursorLeft;
1161                         rl_starty = cursorTop;
1162                         char eof = (char) control_characters [ControlCharacters.EOF];
1163
1164                         do {
1165                                 key = ReadKeyInternal (out fresh);
1166                                 echo = echo || fresh;
1167                                 c = key.KeyChar;
1168                                 // EOF -> Ctrl-D (EOT) pressed.
1169                                 if (c == eof && c != 0 && builder.Length == 0)
1170                                         return null;
1171
1172                                 if (key.Key != ConsoleKey.Enter) {
1173                                         if (key.Key != ConsoleKey.Backspace) {
1174                                                 builder.Append (c);
1175                                         } else if (builder.Length > 0) {
1176                                                 builder.Length--;
1177                                         } else {
1178                                                 // skips over echoing the key to the console
1179                                                 continue;
1180                                         }
1181                                 }
1182
1183                                 // echo fresh keys back to the console
1184                                 if (echo)
1185                                         Echo (key);
1186                         } while (key.Key != ConsoleKey.Enter);
1187
1188                         EchoFlush ();
1189
1190                         rl_startx = -1;
1191                         rl_starty = -1;
1192
1193                         return builder.ToString ();
1194                 }
1195
1196                 public void ResetColor ()
1197                 {
1198                         if (!inited) {
1199                                 Init ();
1200                         }
1201
1202                         string str = (origPair != null) ? origPair : origColors;
1203                         WriteConsole (str);
1204                 }
1205
1206                 public void SetBufferSize (int width, int height)
1207                 {
1208                         if (!inited) {
1209                                 Init ();
1210                         }
1211
1212                         throw new NotImplementedException (String.Empty);
1213                 }
1214
1215                 public void SetCursorPosition (int left, int top)
1216                 {
1217                         if (!inited) {
1218                                 Init ();
1219                         }
1220
1221                         CheckWindowDimensions ();
1222                         if (left < 0 || left >= bufferWidth)
1223                                 throw new ArgumentOutOfRangeException ("left", "Value must be positive and below the buffer width.");
1224
1225                         if (top < 0 || top >= bufferHeight)
1226                                 throw new ArgumentOutOfRangeException ("top", "Value must be positive and below the buffer height.");
1227
1228                         // Either CursorAddress or nothing.
1229                         // We might want to play with up/down/left/right/home when ca is not available.
1230                         if (cursorAddress == null)
1231                                 throw new NotSupportedException ("This terminal does not suport setting the cursor position.");
1232
1233                         int one = (home_1_1 ? 1 : 0);
1234                         WriteConsole (String.Format (cursorAddress, top + one, left + one));
1235                         cursorLeft = left;
1236                         cursorTop = top;
1237                 }
1238
1239                 public void SetWindowPosition (int left, int top)
1240                 {
1241                         if (!inited) {
1242                                 Init ();
1243                         }
1244
1245                         // No need to throw exceptions here.
1246                         //throw new NotSupportedException ();
1247                 }
1248
1249                 public void SetWindowSize (int width, int height)
1250                 {
1251                         if (!inited) {
1252                                 Init ();
1253                         }
1254
1255                         // No need to throw exceptions here.
1256                         //throw new NotSupportedException ();
1257                 }
1258
1259
1260                 void CreateKeyMap ()
1261                 {
1262                         keymap = new Hashtable ();
1263                         
1264                         keymap [TermInfoStrings.KeyBackspace] = new ConsoleKeyInfo ('\0', ConsoleKey.Backspace, false, false, false);
1265                         keymap [TermInfoStrings.KeyClear] = new ConsoleKeyInfo ('\0', ConsoleKey.Clear, false, false, false);
1266                         // Delete character...
1267                         keymap [TermInfoStrings.KeyDown] = new ConsoleKeyInfo ('\0', ConsoleKey.DownArrow, false, false, false);
1268                         keymap [TermInfoStrings.KeyF1] = new ConsoleKeyInfo ('\0', ConsoleKey.F1, false, false, false);
1269                         keymap [TermInfoStrings.KeyF10] = new ConsoleKeyInfo ('\0', ConsoleKey.F10, false, false, false);
1270                         keymap [TermInfoStrings.KeyF2] = new ConsoleKeyInfo ('\0', ConsoleKey.F2, false, false, false);
1271                         keymap [TermInfoStrings.KeyF3] = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false);
1272                         keymap [TermInfoStrings.KeyF4] = new ConsoleKeyInfo ('\0', ConsoleKey.F4, false, false, false);
1273                         keymap [TermInfoStrings.KeyF5] = new ConsoleKeyInfo ('\0', ConsoleKey.F5, false, false, false);
1274                         keymap [TermInfoStrings.KeyF6] = new ConsoleKeyInfo ('\0', ConsoleKey.F6, false, false, false);
1275                         keymap [TermInfoStrings.KeyF7] = new ConsoleKeyInfo ('\0', ConsoleKey.F7, false, false, false);
1276                         keymap [TermInfoStrings.KeyF8] = new ConsoleKeyInfo ('\0', ConsoleKey.F8, false, false, false);
1277                         keymap [TermInfoStrings.KeyF9] = new ConsoleKeyInfo ('\0', ConsoleKey.F9, false, false, false);
1278                         keymap [TermInfoStrings.KeyHome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
1279                         keymap [TermInfoStrings.KeyLeft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, false, false, false);
1280                         keymap [TermInfoStrings.KeyLl] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
1281                         keymap [TermInfoStrings.KeyNpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
1282                         keymap [TermInfoStrings.KeyPpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
1283                         keymap [TermInfoStrings.KeyRight] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, false, false, false);
1284                         keymap [TermInfoStrings.KeySf] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
1285                         keymap [TermInfoStrings.KeySr] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
1286                         keymap [TermInfoStrings.KeyUp] = new ConsoleKeyInfo ('\0', ConsoleKey.UpArrow, false, false, false);
1287                         keymap [TermInfoStrings.KeyA1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad7, false, false, false);
1288                         keymap [TermInfoStrings.KeyA3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad9, false, false, false);
1289                         keymap [TermInfoStrings.KeyB2] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad5, false, false, false);
1290                         keymap [TermInfoStrings.KeyC1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
1291                         keymap [TermInfoStrings.KeyC3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad3, false, false, false);
1292                         keymap [TermInfoStrings.KeyBtab] = new ConsoleKeyInfo ('\0', ConsoleKey.Tab, true, false, false);
1293                         keymap [TermInfoStrings.KeyBeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
1294                         keymap [TermInfoStrings.KeyCopy] = new ConsoleKeyInfo ('C', ConsoleKey.C, false, true, false);
1295                         keymap [TermInfoStrings.KeyEnd] = new ConsoleKeyInfo ('\0', ConsoleKey.End, false, false, false);
1296                         keymap [TermInfoStrings.KeyEnter] = new ConsoleKeyInfo ('\n', ConsoleKey.Enter, false, false, false);
1297                         keymap [TermInfoStrings.KeyHelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, false, false, false);
1298                         keymap [TermInfoStrings.KeyPrint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, false, false, false);
1299                         keymap [TermInfoStrings.KeyUndo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z , false, true, false);
1300                         keymap [TermInfoStrings.KeySbeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1301                         keymap [TermInfoStrings.KeyScopy] = new ConsoleKeyInfo ('C', ConsoleKey.C , true, true, false);
1302                         keymap [TermInfoStrings.KeySdc] = new ConsoleKeyInfo ('\x9', ConsoleKey.Delete, true, false, false);
1303                         keymap [TermInfoStrings.KeyShelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, true, false, false);
1304                         keymap [TermInfoStrings.KeyShome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1305                         keymap [TermInfoStrings.KeySleft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, true, false, false);
1306                         keymap [TermInfoStrings.KeySprint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, true, false, false);
1307                         keymap [TermInfoStrings.KeySright] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, true, false, false);
1308                         keymap [TermInfoStrings.KeySundo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z, true, false, false);
1309                         keymap [TermInfoStrings.KeyF11] = new ConsoleKeyInfo ('\0', ConsoleKey.F11, false, false, false);
1310                         keymap [TermInfoStrings.KeyF12] = new ConsoleKeyInfo ('\0', ConsoleKey.F12 , false, false, false);
1311                         keymap [TermInfoStrings.KeyF13] = new ConsoleKeyInfo ('\0', ConsoleKey.F13, false, false, false);
1312                         keymap [TermInfoStrings.KeyF14] = new ConsoleKeyInfo ('\0', ConsoleKey.F14, false, false, false);
1313                         keymap [TermInfoStrings.KeyF15] = new ConsoleKeyInfo ('\0', ConsoleKey.F15, false, false, false);
1314                         keymap [TermInfoStrings.KeyF16] = new ConsoleKeyInfo ('\0', ConsoleKey.F16, false, false, false);
1315                         keymap [TermInfoStrings.KeyF17] = new ConsoleKeyInfo ('\0', ConsoleKey.F17, false, false, false);
1316                         keymap [TermInfoStrings.KeyF18] = new ConsoleKeyInfo ('\0', ConsoleKey.F18, false, false, false);
1317                         keymap [TermInfoStrings.KeyF19] = new ConsoleKeyInfo ('\0', ConsoleKey.F19, false, false, false);
1318                         keymap [TermInfoStrings.KeyF20] = new ConsoleKeyInfo ('\0', ConsoleKey.F20, false, false, false);
1319                         keymap [TermInfoStrings.KeyF21] = new ConsoleKeyInfo ('\0', ConsoleKey.F21, false, false, false);
1320                         keymap [TermInfoStrings.KeyF22] = new ConsoleKeyInfo ('\0', ConsoleKey.F22, false, false, false);
1321                         keymap [TermInfoStrings.KeyF23] = new ConsoleKeyInfo ('\0', ConsoleKey.F23, false, false, false);
1322                         keymap [TermInfoStrings.KeyF24] = new ConsoleKeyInfo ('\0', ConsoleKey.F24, false, false, false);
1323                         // These were previously missing:
1324                         keymap [TermInfoStrings.KeyDc] = new ConsoleKeyInfo ('\0', ConsoleKey.Delete, false, false, false);
1325                         keymap [TermInfoStrings.KeyIc] = new ConsoleKeyInfo ('\0', ConsoleKey.Insert, false, false, false);
1326                 }
1327
1328                 void InitKeys ()
1329                 {
1330                         if (initKeys)
1331                                 return;
1332
1333                         CreateKeyMap ();
1334                         rootmap = new ByteMatcher ();
1335
1336                         //
1337                         // The keys that we know about and use
1338                         //
1339                         var UsedKeys = new [] {
1340                                 TermInfoStrings.KeyBackspace,
1341                                 TermInfoStrings.KeyClear,
1342                                 TermInfoStrings.KeyDown,
1343                                 TermInfoStrings.KeyF1,
1344                                 TermInfoStrings.KeyF10,
1345                                 TermInfoStrings.KeyF2,
1346                                 TermInfoStrings.KeyF3,
1347                                 TermInfoStrings.KeyF4,
1348                                 TermInfoStrings.KeyF5,
1349                                 TermInfoStrings.KeyF6,
1350                                 TermInfoStrings.KeyF7,
1351                                 TermInfoStrings.KeyF8,
1352                                 TermInfoStrings.KeyF9,
1353                                 TermInfoStrings.KeyHome,
1354                                 TermInfoStrings.KeyLeft,
1355                                 TermInfoStrings.KeyLl,
1356                                 TermInfoStrings.KeyNpage,
1357                                 TermInfoStrings.KeyPpage,
1358                                 TermInfoStrings.KeyRight,
1359                                 TermInfoStrings.KeySf,
1360                                 TermInfoStrings.KeySr,
1361                                 TermInfoStrings.KeyUp,
1362                                 TermInfoStrings.KeyA1,
1363                                 TermInfoStrings.KeyA3,
1364                                 TermInfoStrings.KeyB2,
1365                                 TermInfoStrings.KeyC1,
1366                                 TermInfoStrings.KeyC3,
1367                                 TermInfoStrings.KeyBtab,
1368                                 TermInfoStrings.KeyBeg,
1369                                 TermInfoStrings.KeyCopy,
1370                                 TermInfoStrings.KeyEnd,
1371                                 TermInfoStrings.KeyEnter,
1372                                 TermInfoStrings.KeyHelp,
1373                                 TermInfoStrings.KeyPrint,
1374                                 TermInfoStrings.KeyUndo,
1375                                 TermInfoStrings.KeySbeg,
1376                                 TermInfoStrings.KeyScopy,
1377                                 TermInfoStrings.KeySdc,
1378                                 TermInfoStrings.KeyShelp,
1379                                 TermInfoStrings.KeyShome,
1380                                 TermInfoStrings.KeySleft,
1381                                 TermInfoStrings.KeySprint,
1382                                 TermInfoStrings.KeySright,
1383                                 TermInfoStrings.KeySundo,
1384                                 TermInfoStrings.KeyF11,
1385                                 TermInfoStrings.KeyF12,
1386                                 TermInfoStrings.KeyF13,
1387                                 TermInfoStrings.KeyF14,
1388                                 TermInfoStrings.KeyF15,
1389                                 TermInfoStrings.KeyF16,
1390                                 TermInfoStrings.KeyF17,
1391                                 TermInfoStrings.KeyF18,
1392                                 TermInfoStrings.KeyF19,
1393                                 TermInfoStrings.KeyF20,
1394                                 TermInfoStrings.KeyF21,
1395                                 TermInfoStrings.KeyF22,
1396                                 TermInfoStrings.KeyF23,
1397                                 TermInfoStrings.KeyF24,
1398
1399                                 // These were missing
1400                                 TermInfoStrings.KeyDc,
1401                                 TermInfoStrings.KeyIc
1402                         };
1403                         
1404                         foreach (TermInfoStrings tis in UsedKeys)
1405                                 AddStringMapping (tis);
1406                         
1407                         rootmap.AddMapping (TermInfoStrings.KeyBackspace, new byte [] { control_characters [ControlCharacters.Erase] });
1408                         rootmap.Sort ();
1409                         initKeys = true;
1410                 }
1411
1412                 void AddStringMapping (TermInfoStrings s)
1413                 {
1414                         byte [] bytes = reader.GetStringBytes (s);
1415                         if (bytes == null)
1416                                 return;
1417
1418                         rootmap.AddMapping (s, bytes);
1419                 }
1420         }
1421
1422         class ByteMatcher {
1423                 Hashtable map = new Hashtable ();
1424                 Hashtable starts = new Hashtable ();
1425
1426                 public void AddMapping (TermInfoStrings key, byte [] val)
1427                 {
1428                         if (val.Length == 0)
1429                                 return;
1430
1431                         map [val] = key;
1432                         starts [(int) val [0]] = true;
1433                 }
1434
1435                 public void Sort ()
1436                 {
1437                 }
1438
1439                 public bool StartsWith (int c)
1440                 {
1441                         return (starts [c] != null);
1442                 }
1443
1444                 public TermInfoStrings Match (char [] buffer, int offset, int length, out int used)
1445                 {
1446                         foreach (byte [] bytes in map.Keys) {
1447                                 for (int i = 0; i < bytes.Length && i < length; i++) {
1448                                         if ((char) bytes [i] != buffer [offset + i])
1449                                                 break;
1450
1451                                         if (bytes.Length - 1 == i) {
1452                                                 used = bytes.Length;
1453                                                 return (TermInfoStrings) map [bytes];
1454                                         }
1455                                 }
1456                         }
1457
1458                         used = 0;
1459                         return (TermInfoStrings) (-1);
1460                 }
1461         }
1462 }
1463 #endif
1464