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