2006-09-19 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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 namespace System {
35         class TermInfoDriver : IConsoleDriver {
36                 static string [] locations = { "/etc/terminfo", "/usr/share/terminfo", "/usr/lib/terminfo" };
37
38                 TermInfoReader reader;
39                 int cursorLeft;
40                 int cursorTop;
41                 string title = String.Empty;
42                 string titleFormat = String.Empty;
43                 bool cursorVisible = true;
44                 string csrVisible;
45                 string csrInvisible;
46                 string clear;
47                 string bell;
48                 string term;
49                 Stream stdout;
50                 Stream stdin;
51                 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                         Console.Write (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                         Init ();
140                 }
141
142                 public bool Initialized {
143                         get { return inited; }
144                 }
145                 
146                 void Init ()
147                 {
148                         if (inited)
149                                 return;
150                         
151                         /* This should not happen any more, since it is checked for in Console */
152                         if (!ConsoleDriver.IsConsole)
153                                 throw new IOException ("Not a tty.");
154
155                         inited = true;
156                         ConsoleDriver.SetEcho (false);
157                         string endString = null;
158                         keypadXmit = reader.Get (TermInfoStrings.KeypadXmit);
159                         keypadLocal = reader.Get (TermInfoStrings.KeypadLocal);
160                         if (keypadXmit != null) {
161                                 WriteConsole (keypadXmit); // Needed to get the arrows working
162                                 if (keypadLocal != null)
163                                         endString += keypadLocal;
164                         }
165
166                         origPair = reader.Get (TermInfoStrings.OrigPair);
167                         origColors = reader.Get (TermInfoStrings.OrigColors);
168                         setafcolor = MangleParameters (reader.Get (TermInfoStrings.SetAForeground));
169                         setabcolor = MangleParameters (reader.Get (TermInfoStrings.SetABackground));
170                         string resetColors = (origColors == null) ? origPair : origColors;
171                         if (resetColors != null)
172                                 endString += resetColors;
173
174                         if (!ConsoleDriver.TtySetup (endString, out verase, out vsusp, out intr))
175                                 throw new IOException ("Error initializing terminal.");
176
177                         stdout = Console.OpenStandardOutput (0);
178                         stdin = Console.OpenStandardInput (0);
179                         clear = reader.Get (TermInfoStrings.ClearScreen);
180                         bell = reader.Get (TermInfoStrings.Bell);
181                         if (clear == null) {
182                                 clear = reader.Get (TermInfoStrings.CursorHome);
183                                 clear += reader.Get (TermInfoStrings.ClrEos);
184                         }
185
186                         csrVisible = reader.Get (TermInfoStrings.CursorNormal);
187                         if (csrVisible == null)
188                                 csrVisible = reader.Get (TermInfoStrings.CursorVisible);
189
190                         csrInvisible = reader.Get (TermInfoStrings.CursorInvisible);
191                         if (term == "cygwin" || term == "linux" || term.StartsWith ("xterm") ||
192                                 term == "rxvt" || term == "dtterm") {
193                                 titleFormat = "\x1b]0;{0}\x7"; // icon + window title
194                         } else if (term == "iris-ansi") {
195                                 titleFormat = "\x1bP1.y{0}\x1b\\"; // not tested
196                         } else if (term == "sun-cmd") {
197                                 titleFormat = "\x1b]l{0}\x1b\\"; // not tested
198                         }
199
200                         cursorAddress = reader.Get (TermInfoStrings.CursorAddress);
201                         if (cursorAddress != null) {
202                                 string result = cursorAddress.Replace ("%i", String.Empty);
203                                 home_1_1 = (cursorAddress != result);
204                                 cursorAddress = MangleParameters (result);
205                         }
206
207                         GetCursorPosition ();
208                         if (noGetPosition) {
209                                 WriteConsole (clear);
210                                 cursorLeft = 0;
211                                 cursorTop = 0;
212                         }
213                 }
214
215                 static string MangleParameters (string str)
216                 {
217                         if (str == null)
218                                 return null;
219
220                         str = str.Replace ("{", "{{");
221                         str = str.Replace ("}", "}}");
222                         str = str.Replace ("%p1%d", "{0}");
223                         return str.Replace ("%p2%d", "{1}");
224                 }
225
226                 static int TranslateColor (ConsoleColor desired)
227                 {
228                         switch (desired) {
229                         case ConsoleColor.Black:
230                         case ConsoleColor.DarkGray:
231                                 return 0;
232                         case ConsoleColor.DarkBlue:
233                         case ConsoleColor.Blue:
234                                 return 4;
235                         case ConsoleColor.DarkGreen:
236                         case ConsoleColor.Green:
237                                 return 2;
238                         case ConsoleColor.DarkCyan:
239                         case ConsoleColor.Cyan:
240                                 return 6;
241                         case ConsoleColor.DarkRed:
242                         case ConsoleColor.Red:
243                                 return 1;
244                         case ConsoleColor.DarkMagenta:
245                         case ConsoleColor.Magenta:
246                                 return 5;
247                         case ConsoleColor.DarkYellow:
248                         case ConsoleColor.Yellow:
249                                 return 3;
250                         case ConsoleColor.Gray:
251                         case ConsoleColor.White:
252                                 return 7;
253                         }
254
255                         return 0;
256                 }
257
258                 public ConsoleColor BackgroundColor {
259                         get { return bgcolor; }
260                         set {
261                                 bgcolor = value;
262                                 WriteConsole (String.Format (setabcolor, TranslateColor (value)));
263                         }
264                 }
265
266                 public ConsoleColor ForegroundColor {
267                         get { return fgcolor; }
268                         set {
269                                 fgcolor = value;
270                                 WriteConsole (String.Format (setafcolor, TranslateColor (value)));
271                         }
272                 }
273
274                 // Only used once.
275                 void GetCursorPosition ()
276                 {
277                         int row = 0, col = 0;
278                         bool prevEcho = Echo;
279                         Echo = false;
280                         try {
281                                 WriteConsole ("\x1b[6n");
282                                 if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
283                                         noGetPosition = true;
284                                         return;
285                                 }
286
287                                 int b = stdin.ReadByte ();
288                                 while (b != '\x1b') {
289                                         AddToBuffer (b);
290                                         if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
291                                                 return;
292                                         b = stdin.ReadByte ();
293                                 }
294
295                                 b = stdin.ReadByte ();
296                                 if (b != '[') {
297                                         AddToBuffer ('\x1b');
298                                         AddToBuffer (b);
299                                         return;
300                                 }
301
302                                 b = stdin.ReadByte ();
303                                 if (b != ';') {
304                                         row = b - '0';
305                                         b = stdin.ReadByte ();
306                                         while ((b >= '0') && (b <= '9')) {
307                                                 row = row * 10 + b - '0';
308                                                 b = stdin.ReadByte ();
309                                         }
310                                         // Row/col is 0 based
311                                         row --;
312                                 }
313
314                                 b = stdin.ReadByte ();
315                                 if (b != 'R') {
316                                         col = b - '0';
317                                         b = stdin.ReadByte ();
318                                         while ((b >= '0') && (b <= '9')) {
319                                                 col = col * 10 + b - '0';
320                                                 b = stdin.ReadByte ();
321                                         }
322                                         // Row/col is 0 based
323                                         col --;
324                                 }
325                         } finally {
326                                 Echo = prevEcho;
327                         }
328
329                         cursorLeft = col;
330                         cursorTop = row;
331                 }
332
333                 public int BufferHeight {
334                         get {
335                                 return bufferHeight;
336                         }
337                         set {
338                                 throw new NotSupportedException ();
339                         }
340                 }
341
342                 public int BufferWidth {
343                         get {
344                                 return bufferWidth;
345                         }
346                         set {
347                                 throw new NotSupportedException ();
348                         }
349                 }
350
351                 public bool CapsLock {
352                         get { throw new NotSupportedException (); }
353                 }
354
355                 public int CursorLeft {
356                         get {
357                                 GetCursorPosition ();
358                                 return cursorLeft;
359                         }
360                         set {
361                                 SetCursorPosition (value, CursorTop);
362                                 cursorLeft = value;
363                         }
364                 }
365
366                 public int CursorTop {
367                         get {
368                                 GetCursorPosition ();
369                                 return cursorTop;
370                         }
371                         set {
372                                 SetCursorPosition (CursorLeft, value);
373                                 cursorTop = value;
374                         }
375                 }
376
377                 public bool CursorVisible {
378                         get {
379                                 return cursorVisible;
380                         }
381                         set {
382                                 cursorVisible = value;
383                                 WriteConsole ((value ? csrVisible : csrInvisible));
384                         }
385                 }
386
387                 // we have CursorNormal vs. CursorVisible...
388                 [MonoTODO]
389                 public int CursorSize {
390                         get { return 1; }
391                         set {}
392                 }
393
394                 public bool Echo {
395                         get { return !noEcho; }
396                         set {
397                                 if (value != noEcho)
398                                         return;
399
400                                 noEcho = !value;
401                                 ConsoleDriver.SetEcho (value);
402                         }
403                 }
404
405                 public bool KeyAvailable {
406                         get {
407                                 return (writepos > readpos || ConsoleDriver.InternalKeyAvailable (0) > 0);
408                         }
409                 }
410
411                 // We don't know these next 2 values, so return something reasonable
412                 public int LargestWindowHeight {
413                         get { return WindowHeight; }
414                 }
415
416                 public int LargestWindowWidth {
417                         get { return WindowWidth; }
418                 }
419
420                 public bool NumberLock {
421                         get { throw new NotSupportedException (); }
422                 }
423
424                 public string Title {
425                         get { return title; }
426                         
427                         set {
428                                 title = value;
429                                 WriteConsole (String.Format (titleFormat, value));
430                         }
431                 }
432
433                 public bool TreatControlCAsInput {
434                         get { return controlCAsInput; }
435                         set {
436                                 if (controlCAsInput == value)
437                                         return;
438
439                                 ConsoleDriver.SetBreak (value);
440                                 controlCAsInput = value;
441                         }
442                 }
443
444                 // TODO: use this at the beginning and on every SIGWINCH
445                 void GetWindowDimensions ()
446                 {
447                         /* Try the ioctl first */
448                         if (!ConsoleDriver.GetTtySize (MonoIO.ConsoleOutput, out windowWidth, out windowHeight)) {
449                                 windowWidth = reader.Get (TermInfoNumbers.Columns);
450                                 string env = Environment.GetEnvironmentVariable ("COLUMNS");
451                                 if (env != null) {
452                                         try {
453                                                 windowWidth = (int) UInt32.Parse (env);
454                                         } catch {
455                                         }
456                                 }
457
458                                 windowHeight = reader.Get (TermInfoNumbers.Lines);
459                                 env = Environment.GetEnvironmentVariable ("LINES");
460                                 if (env != null) {
461                                         try {
462                                                 windowHeight = (int) UInt32.Parse (env);
463                                         } catch {
464                                         }
465                                 }
466                         }
467
468                         bufferHeight = windowHeight;
469                         bufferWidth = windowWidth;
470                 }
471
472                 public int WindowHeight {
473                         get {
474                                 GetWindowDimensions ();
475                                 return windowHeight;
476                         }
477                         set {
478                                 throw new NotSupportedException ();
479                         }
480                 }
481
482                 public int WindowLeft {
483                         get {
484                                 //GetWindowDimensions ();
485                                 return 0;
486                         }
487                         set {
488                                 throw new NotSupportedException ();
489                         }
490                 }
491
492                 public int WindowTop {
493                         get {
494                                 //GetWindowDimensions ();
495                                 return 0;
496                         }
497                         set {
498                                 throw new NotSupportedException ();
499                         }
500                 }
501
502                 public int WindowWidth {
503                         get {
504                                 GetWindowDimensions ();
505                                 return windowWidth;
506                         }
507                         set {
508                                 throw new NotSupportedException ();
509                         }
510                 }
511
512                 public void Clear ()
513                 {
514                         WriteConsole (clear);
515                 }
516
517                 public void Beep (int frequency, int duration)
518                 {
519                         WriteConsole (bell);
520                 }
521
522                 public void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
523                                         int targetLeft, int targetTop, Char sourceChar,
524                                         ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
525                 {
526                         throw new NotImplementedException ();
527                 }
528
529                 void AddToBuffer (int b)
530                 {
531                         if (buffer == null)
532                                 buffer = new byte [1024];
533
534                         buffer [writepos++] = (byte) b;
535                 }
536
537                 ConsoleKeyInfo CreateKeyInfoFromInt (int n)
538                 {
539                         char c = (char) n;
540                         ConsoleKey key = (ConsoleKey)n;
541                         bool shift = false;
542                         bool ctrl = false;
543                         bool alt = false;
544
545                         if (n == 10) {
546                                 key = ConsoleKey.Enter;
547                         } else if (n == 8 || n == 9 || n == 12 || n == 13 || n == 19) {
548                                 /* Values in ConsoleKey */
549                         } else if (n >= 1 && n <= 26) {
550                                 // For Ctrl-a to Ctrl-z.
551                                 ctrl = true;
552                                 key = ConsoleKey.A + n - 1;
553                         } else if (n == 27) {
554                                 key = ConsoleKey.Escape;
555                         } else if (n >= 'a' && n <= 'z') {
556                                 key = ConsoleKey.A - 'a' + n;
557                         } else if (n >= 'A' && n <= 'Z') {
558                                 shift = true;
559                         } else if (n >= '0' && n <= '9') {
560                         } else {
561                                 key = 0;
562                         }
563
564                         return new ConsoleKeyInfo (c, key, shift, alt, ctrl);
565                 }
566
567                 object GetKeyFromBuffer (bool cooked)
568                 {
569                         if (readpos >= writepos)
570                                 return null;
571
572                         int next = buffer [readpos];
573                         if (!cooked || !rootmap.StartsWith (next)) {
574                                 readpos++;
575                                 return CreateKeyInfoFromInt (next);
576                         }
577
578                         int used;
579                         TermInfoStrings str = rootmap.Match (buffer, readpos, writepos - readpos, out used);
580                         if ((int) str == -1)
581                                 return null;
582
583                         ConsoleKeyInfo key;
584                         if (keymap [str] != null) {
585                                 key = (ConsoleKeyInfo) keymap [str];
586                         } else {
587                                 readpos++;
588                                 return CreateKeyInfoFromInt (next);
589                         }
590
591                         readpos += used;
592                         if (readpos >= writepos)
593                                 readpos = writepos = 0;
594
595                         return key;
596                 }
597
598                 ConsoleKeyInfo ReadKeyInternal ()
599                 {
600                         InitKeys ();
601                         object o = null;
602                         while (o == null) {
603                                 o = GetKeyFromBuffer (true);
604                                 if (o == null) {
605                                         if (ConsoleDriver.InternalKeyAvailable (150) > 0) {
606                                                 do {
607                                                         AddToBuffer (stdin.ReadByte ());
608                                                 } while (ConsoleDriver.InternalKeyAvailable (0) > 0);
609                                         } else {
610                                                 o = GetKeyFromBuffer (false);
611                                                 if (o == null)
612                                                         AddToBuffer (stdin.ReadByte ());
613                                         }
614                                 }
615                         }
616
617                         return (ConsoleKeyInfo) o;
618                 }
619
620                 public ConsoleKeyInfo ReadKey (bool intercept)
621                 {
622                         bool prevEcho = Echo;
623                         if (prevEcho == intercept)
624                                 Echo  = !intercept;
625
626                         ConsoleKeyInfo key = ReadKeyInternal ();
627                         if (prevEcho == intercept)
628                                 Echo = prevEcho;
629
630                         return key;
631                 }
632
633                 public string ReadLine ()
634                 {
635                         bool prevEcho = Echo;
636                         if (prevEcho == false)
637                                 Echo  = true;
638                         StringBuilder builder = new StringBuilder ();
639                         bool exit = false;
640                         do {
641                                 ConsoleKeyInfo key = ReadKeyInternal ();
642                                 char c = key.KeyChar;
643                                 exit = (c == '\n');
644                                 if (!exit)
645                                         builder.Append (c);
646                                 stdout.WriteByte ((byte) c);
647                         } while (!exit);
648                         if (prevEcho == false)
649                                 Echo = prevEcho;
650                         return builder.ToString ();
651                 }
652
653                 public void ResetColor ()
654                 {
655                         string str = (origPair != null) ? origPair : origColors;
656                         WriteConsole (str);
657                 }
658
659                 public void SetBufferSize (int width, int height)
660                 {
661                         throw new NotImplementedException (String.Empty);
662                 }
663
664                 public void SetCursorPosition (int left, int top)
665                 {
666                         if (bufferWidth == 0)
667                                 GetWindowDimensions ();
668
669                         if (left < 0 || left >= bufferWidth)
670                                 throw new ArgumentOutOfRangeException ("left", "Value must be positive and below the buffer width.");
671
672                         if (top < 0 || top >= bufferHeight)
673                                 throw new ArgumentOutOfRangeException ("top", "Value must be positive and below the buffer height.");
674
675                         // Either CursorAddress or nothing.
676                         // We might want to play with up/down/left/right/home when ca is not available.
677                         if (cursorAddress == null)
678                                 throw new NotSupportedException ("This terminal does not suport setting the cursor position.");
679
680                         int one = (home_1_1 ? 1 : 0);
681                         WriteConsole (String.Format (cursorAddress, top + one, left + one));
682                         cursorLeft = left;
683                         cursorTop = top;
684                 }
685
686                 public void SetWindowPosition (int left, int top)
687                 {
688                         // No need to throw exceptions here.
689                         //throw new NotSupportedException ();
690                 }
691
692                 public void SetWindowSize (int width, int height)
693                 {
694                         // No need to throw exceptions here.
695                         //throw new NotSupportedException ();
696                 }
697
698
699                 void CreateKeyMap ()
700                 {
701                         keymap = new Hashtable ();
702                         keymap [TermInfoStrings.KeyBackspace] = new ConsoleKeyInfo ('\0', ConsoleKey.Backspace, false, false, false);
703                         keymap [TermInfoStrings.KeyClear] = new ConsoleKeyInfo ('\0', ConsoleKey.Clear, false, false, false);
704                         // Delete character...
705                         keymap [TermInfoStrings.KeyDown] = new ConsoleKeyInfo ('\0', ConsoleKey.DownArrow, false, false, false);
706                         keymap [TermInfoStrings.KeyF1] = new ConsoleKeyInfo ('\0', ConsoleKey.F1, false, false, false);
707                         keymap [TermInfoStrings.KeyF10] = new ConsoleKeyInfo ('\0', ConsoleKey.F10, false, false, false);
708                         keymap [TermInfoStrings.KeyF2] = new ConsoleKeyInfo ('\0', ConsoleKey.F2, false, false, false);
709                         keymap [TermInfoStrings.KeyF3] = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false);
710                         keymap [TermInfoStrings.KeyF4] = new ConsoleKeyInfo ('\0', ConsoleKey.F4, false, false, false);
711                         keymap [TermInfoStrings.KeyF5] = new ConsoleKeyInfo ('\0', ConsoleKey.F5, false, false, false);
712                         keymap [TermInfoStrings.KeyF6] = new ConsoleKeyInfo ('\0', ConsoleKey.F6, false, false, false);
713                         keymap [TermInfoStrings.KeyF7] = new ConsoleKeyInfo ('\0', ConsoleKey.F7, false, false, false);
714                         keymap [TermInfoStrings.KeyF8] = new ConsoleKeyInfo ('\0', ConsoleKey.F8, false, false, false);
715                         keymap [TermInfoStrings.KeyF9] = new ConsoleKeyInfo ('\0', ConsoleKey.F9, false, false, false);
716                         keymap [TermInfoStrings.KeyHome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
717
718                         keymap [TermInfoStrings.KeyLeft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, false, false, false);
719                         keymap [TermInfoStrings.KeyLl] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
720                         keymap [TermInfoStrings.KeyNpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
721                         keymap [TermInfoStrings.KeyPpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
722                         keymap [TermInfoStrings.KeyRight] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, false, false, false);
723                         keymap [TermInfoStrings.KeySf] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
724                         keymap [TermInfoStrings.KeySr] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
725                         keymap [TermInfoStrings.KeyUp] = new ConsoleKeyInfo ('\0', ConsoleKey.UpArrow, false, false, false);
726                         keymap [TermInfoStrings.KeyA1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad7, false, false, false);
727                         keymap [TermInfoStrings.KeyA3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad9, false, false, false);
728                         keymap [TermInfoStrings.KeyB2] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad5, false, false, false);
729                         keymap [TermInfoStrings.KeyC1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
730                         keymap [TermInfoStrings.KeyC3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad3, false, false, false);
731                         keymap [TermInfoStrings.KeyBtab] = new ConsoleKeyInfo ('\0', ConsoleKey.Tab, true, false, false);
732                         keymap [TermInfoStrings.KeyBeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
733                         keymap [TermInfoStrings.KeyCopy] = new ConsoleKeyInfo ('C', ConsoleKey.C, false, true, false);
734                         keymap [TermInfoStrings.KeyEnd] = new ConsoleKeyInfo ('\0', ConsoleKey.End, false, false, false);
735                         keymap [TermInfoStrings.KeyEnter] = new ConsoleKeyInfo ('\n', ConsoleKey.Enter, false, false, false);
736                         keymap [TermInfoStrings.KeyHelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, false, false, false);
737                         keymap [TermInfoStrings.KeyPrint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, false, false, false);
738                         keymap [TermInfoStrings.KeyUndo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z , false, true, false);
739                         keymap [TermInfoStrings.KeySbeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
740                         keymap [TermInfoStrings.KeyScopy] = new ConsoleKeyInfo ('C', ConsoleKey.C , true, true, false);
741                         keymap [TermInfoStrings.KeySdc] = new ConsoleKeyInfo ('\x9', ConsoleKey.Delete, true, false, false);
742                         keymap [TermInfoStrings.KeyShelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, true, false, false);
743                         keymap [TermInfoStrings.KeyShome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
744                         keymap [TermInfoStrings.KeySleft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, true, false, false);
745                         keymap [TermInfoStrings.KeySprint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, true, false, false);
746                         keymap [TermInfoStrings.KeySright] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, true, false, false);
747                         keymap [TermInfoStrings.KeySundo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z, true, false, false);
748                         keymap [TermInfoStrings.KeyF11] = new ConsoleKeyInfo ('\0', ConsoleKey.F11, false, false, false);
749                         keymap [TermInfoStrings.KeyF12] = new ConsoleKeyInfo ('\0', ConsoleKey.F12 , false, false, false);
750                         keymap [TermInfoStrings.KeyF13] = new ConsoleKeyInfo ('\0', ConsoleKey.F13, false, false, false);
751                         keymap [TermInfoStrings.KeyF14] = new ConsoleKeyInfo ('\0', ConsoleKey.F14, false, false, false);
752                         keymap [TermInfoStrings.KeyF15] = new ConsoleKeyInfo ('\0', ConsoleKey.F15, false, false, false);
753                         keymap [TermInfoStrings.KeyF16] = new ConsoleKeyInfo ('\0', ConsoleKey.F16, false, false, false);
754                         keymap [TermInfoStrings.KeyF17] = new ConsoleKeyInfo ('\0', ConsoleKey.F17, false, false, false);
755                         keymap [TermInfoStrings.KeyF18] = new ConsoleKeyInfo ('\0', ConsoleKey.F18, false, false, false);
756                         keymap [TermInfoStrings.KeyF19] = new ConsoleKeyInfo ('\0', ConsoleKey.F19, false, false, false);
757                         keymap [TermInfoStrings.KeyF20] = new ConsoleKeyInfo ('\0', ConsoleKey.F20, false, false, false);
758                         keymap [TermInfoStrings.KeyF21] = new ConsoleKeyInfo ('\0', ConsoleKey.F21, false, false, false);
759                         keymap [TermInfoStrings.KeyF22] = new ConsoleKeyInfo ('\0', ConsoleKey.F22, false, false, false);
760                         keymap [TermInfoStrings.KeyF23] = new ConsoleKeyInfo ('\0', ConsoleKey.F23, false, false, false);
761                         keymap [TermInfoStrings.KeyF24] = new ConsoleKeyInfo ('\0', ConsoleKey.F24, false, false, false);
762                 }
763
764                 void InitKeys ()
765                 {
766                         if (initKeys)
767                                 return;
768
769                         CreateKeyMap ();
770                         rootmap = new ByteMatcher ();
771                         AddStringMapping (TermInfoStrings.KeyBackspace);
772                         AddStringMapping (TermInfoStrings.KeyClear);
773                         AddStringMapping (TermInfoStrings.KeyDown);
774                         AddStringMapping (TermInfoStrings.KeyF1);
775                         AddStringMapping (TermInfoStrings.KeyF10);
776                         AddStringMapping (TermInfoStrings.KeyF2);
777                         AddStringMapping (TermInfoStrings.KeyF3);
778                         AddStringMapping (TermInfoStrings.KeyF4);
779                         AddStringMapping (TermInfoStrings.KeyF5);
780                         AddStringMapping (TermInfoStrings.KeyF6);
781                         AddStringMapping (TermInfoStrings.KeyF7);
782                         AddStringMapping (TermInfoStrings.KeyF8);
783                         AddStringMapping (TermInfoStrings.KeyF9);
784                         AddStringMapping (TermInfoStrings.KeyHome);
785                         AddStringMapping (TermInfoStrings.KeyLeft);
786                         AddStringMapping (TermInfoStrings.KeyLl);
787                         AddStringMapping (TermInfoStrings.KeyNpage);
788                         AddStringMapping (TermInfoStrings.KeyPpage);
789                         AddStringMapping (TermInfoStrings.KeyRight);
790                         AddStringMapping (TermInfoStrings.KeySf);
791                         AddStringMapping (TermInfoStrings.KeySr);
792                         AddStringMapping (TermInfoStrings.KeyUp);
793                         AddStringMapping (TermInfoStrings.KeyA1);
794                         AddStringMapping (TermInfoStrings.KeyA3);
795                         AddStringMapping (TermInfoStrings.KeyB2);
796                         AddStringMapping (TermInfoStrings.KeyC1);
797                         AddStringMapping (TermInfoStrings.KeyC3);
798                         AddStringMapping (TermInfoStrings.KeyBtab);
799                         AddStringMapping (TermInfoStrings.KeyBeg);
800                         AddStringMapping (TermInfoStrings.KeyCopy);
801                         AddStringMapping (TermInfoStrings.KeyEnd);
802                         AddStringMapping (TermInfoStrings.KeyEnter);
803                         AddStringMapping (TermInfoStrings.KeyHelp);
804                         AddStringMapping (TermInfoStrings.KeyPrint);
805                         AddStringMapping (TermInfoStrings.KeyUndo);
806                         AddStringMapping (TermInfoStrings.KeySbeg);
807                         AddStringMapping (TermInfoStrings.KeyScopy);
808                         AddStringMapping (TermInfoStrings.KeySdc);
809                         AddStringMapping (TermInfoStrings.KeyShelp);
810                         AddStringMapping (TermInfoStrings.KeyShome);
811                         AddStringMapping (TermInfoStrings.KeySleft);
812                         AddStringMapping (TermInfoStrings.KeySprint);
813                         AddStringMapping (TermInfoStrings.KeySright);
814                         AddStringMapping (TermInfoStrings.KeySundo);
815                         AddStringMapping (TermInfoStrings.KeyF11);
816                         AddStringMapping (TermInfoStrings.KeyF12);
817                         AddStringMapping (TermInfoStrings.KeyF13);
818                         AddStringMapping (TermInfoStrings.KeyF14);
819                         AddStringMapping (TermInfoStrings.KeyF15);
820                         AddStringMapping (TermInfoStrings.KeyF16);
821                         AddStringMapping (TermInfoStrings.KeyF17);
822                         AddStringMapping (TermInfoStrings.KeyF18);
823                         AddStringMapping (TermInfoStrings.KeyF19);
824                         AddStringMapping (TermInfoStrings.KeyF20);
825                         AddStringMapping (TermInfoStrings.KeyF21);
826                         AddStringMapping (TermInfoStrings.KeyF22);
827                         AddStringMapping (TermInfoStrings.KeyF23);
828                         AddStringMapping (TermInfoStrings.KeyF24);
829                         rootmap.Sort ();
830                         initKeys = true;
831                 }
832
833                 void AddStringMapping (TermInfoStrings s)
834                 {
835                         byte [] bytes = reader.GetStringBytes (s);
836                         if (bytes == null)
837                                 return;
838
839                         rootmap.AddMapping (s, bytes);
840                 }
841         }
842
843         class ByteMatcher {
844                 Hashtable map = new Hashtable ();
845                 Hashtable starts = new Hashtable ();
846
847                 public void AddMapping (TermInfoStrings key, byte [] val)
848                 {
849                         if (val.Length == 0)
850                                 return;
851
852                         map [val] = key;
853                         starts [(int) val [0]] = true;
854                 }
855
856                 public void Sort ()
857                 {
858                 }
859
860                 public bool StartsWith (int c)
861                 {
862                         return (starts [c] != null);
863                 }
864
865                 public TermInfoStrings Match (byte [] buffer, int offset, int length, out int used)
866                 {
867                         foreach (byte [] bytes in map.Keys) {
868                                 for (int i = 0; i < bytes.Length && i < length; i++) {
869                                         if (bytes [i] != buffer [offset + i])
870                                                 break;
871
872                                         if (bytes.Length - 1 == i) {
873                                                 used = bytes.Length;
874                                                 return (TermInfoStrings) map [bytes];
875                                         }
876                                 }
877                         }
878
879                         used = 0;
880                         return (TermInfoStrings) (-1);
881                 }
882         }
883 }
884 #endif
885