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