98f2833d42a2dea53bb8bf6e1e5cb557237fc45d
[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                 void GetCursorPosition ()
397                 {
398                         int row = 0, col = 0;
399
400                         WriteConsole ("\x1b[6n");
401                         if (ConsoleDriver.InternalKeyAvailable (1000) <= 0) {
402                                 noGetPosition = true;
403                                 return;
404                         }
405
406                         int b = stdin.ReadByte ();
407                         while (b != '\x1b') {
408                                 AddToBuffer (b);
409                                 if (ConsoleDriver.InternalKeyAvailable (100) <= 0)
410                                         return;
411                                 b = stdin.ReadByte ();
412                         }
413
414                         b = stdin.ReadByte ();
415                         if (b != '[') {
416                                 AddToBuffer ('\x1b');
417                                 AddToBuffer (b);
418                                 return;
419                         }
420
421                         b = stdin.ReadByte ();
422                         if (b != ';') {
423                                 row = b - '0';
424                                 b = stdin.ReadByte ();
425                                 while ((b >= '0') && (b <= '9')) {
426                                         row = row * 10 + b - '0';
427                                         b = stdin.ReadByte ();
428                                 }
429                                 // Row/col is 0 based
430                                 row --;
431                         }
432
433                         b = stdin.ReadByte ();
434                         if (b != 'R') {
435                                 col = b - '0';
436                                 b = stdin.ReadByte ();
437                                 while ((b >= '0') && (b <= '9')) {
438                                         col = col * 10 + b - '0';
439                                         b = stdin.ReadByte ();
440                                 }
441                                 // Row/col is 0 based
442                                 col --;
443                         }
444
445 #if DEBUG
446                         logger.WriteLine ("GetCursorPosition: {0}, {1}", col, row);
447                         logger.Flush ();
448 #endif
449
450                         cursorLeft = col;
451                         cursorTop = row;
452                 }
453
454                 public int BufferHeight {
455                         get {
456                                 if (!inited) {
457                                         Init ();
458                                 }
459
460                                 return bufferHeight;
461                         }
462                         set {
463                                 if (!inited) {
464                                         Init ();
465                                 }
466
467                                 throw new NotSupportedException ();
468                         }
469                 }
470
471                 public int BufferWidth {
472                         get {
473                                 if (!inited) {
474                                         Init ();
475                                 }
476
477                                 return bufferWidth;
478                         }
479                         set {
480                                 if (!inited) {
481                                         Init ();
482                                 }
483
484                                 throw new NotSupportedException ();
485                         }
486                 }
487
488                 public bool CapsLock {
489                         get {
490                                 if (!inited) {
491                                         Init ();
492                                 }
493
494                                 throw new NotSupportedException ();
495                         }
496                 }
497
498                 public int CursorLeft {
499                         get {
500                                 if (!inited) {
501                                         Init ();
502                                 }
503
504                                 return cursorLeft;
505                         }
506                         set {
507                                 if (!inited) {
508                                         Init ();
509                                 }
510
511                                 SetCursorPosition (value, CursorTop);
512                         }
513                 }
514
515                 public int CursorTop {
516                         get {
517                                 if (!inited) {
518                                         Init ();
519                                 }
520
521                                 return cursorTop;
522                         }
523                         set {
524                                 if (!inited) {
525                                         Init ();
526                                 }
527
528                                 SetCursorPosition (CursorLeft, value);
529                         }
530                 }
531
532                 public bool CursorVisible {
533                         get {
534                                 if (!inited) {
535                                         Init ();
536                                 }
537
538                                 return cursorVisible;
539                         }
540                         set {
541                                 if (!inited) {
542                                         Init ();
543                                 }
544
545                                 cursorVisible = value;
546                                 WriteConsole ((value ? csrVisible : csrInvisible));
547                         }
548                 }
549
550                 // we have CursorNormal vs. CursorVisible...
551                 [MonoTODO]
552                 public int CursorSize {
553                         get {
554                                 if (!inited) {
555                                         Init ();
556                                 }
557                                 return 1;
558                         }
559                         set {
560                                 if (!inited) {
561                                         Init ();
562                                 }
563                         }
564
565                 }
566
567                 public bool KeyAvailable {
568                         get {
569                                 if (!inited) {
570                                         Init ();
571                                 }
572
573                                 return (writepos > readpos || ConsoleDriver.InternalKeyAvailable (0) > 0);
574                         }
575                 }
576
577                 // We don't know these next 2 values, so return something reasonable
578                 public int LargestWindowHeight {
579                         get { return WindowHeight; }
580                 }
581
582                 public int LargestWindowWidth {
583                         get { return WindowWidth; }
584                 }
585
586                 public bool NumberLock {
587                         get {
588                                 if (!inited) {
589                                         Init ();
590                                 }
591
592                                 throw new NotSupportedException ();
593                         }
594                 }
595
596                 public string Title {
597                         get {
598                                 if (!inited) {
599                                         Init ();
600                                 }
601                                 return title;
602                         }
603                         
604                         set {
605                                 if (!inited) {
606                                         Init ();
607                                 }
608
609                                 title = value;
610                                 WriteConsole (String.Format (titleFormat, value));
611                         }
612                 }
613
614                 public bool TreatControlCAsInput {
615                         get {
616                                 if (!inited) {
617                                         Init ();
618                                 }
619                                 return controlCAsInput;
620                         }
621                         set {
622                                 if (!inited) {
623                                         Init ();
624                                 }
625
626                                 if (controlCAsInput == value)
627                                         return;
628
629                                 ConsoleDriver.SetBreak (value);
630                                 controlCAsInput = value;
631                         }
632                 }
633
634                 void GetWindowDimensions ()
635                 {
636                         /* Try the ioctl first */
637                         if (!ConsoleDriver.GetTtySize (MonoIO.ConsoleOutput, out windowWidth, out windowHeight)) {
638                                 windowWidth = reader.Get (TermInfoNumbers.Columns);
639                                 string env = Environment.GetEnvironmentVariable ("COLUMNS");
640                                 if (env != null) {
641                                         try {
642                                                 windowWidth = (int) UInt32.Parse (env);
643                                         } catch {
644                                         }
645                                 }
646
647                                 windowHeight = reader.Get (TermInfoNumbers.Lines);
648                                 env = Environment.GetEnvironmentVariable ("LINES");
649                                 if (env != null) {
650                                         try {
651                                                 windowHeight = (int) UInt32.Parse (env);
652                                         } catch {
653                                         }
654                                 }
655                         }
656
657                         bufferHeight = windowHeight;
658                         bufferWidth = windowWidth;
659                         need_window_dimensions = false;
660                 }
661
662                 public int WindowHeight {
663                         get {
664                                 if (!inited) {
665                                         Init ();
666                                 }
667
668                                 if (need_window_dimensions)
669                                         GetWindowDimensions ();
670                                 return windowHeight;
671                         }
672                         set {
673                                 if (!inited) {
674                                         Init ();
675                                 }
676
677                                 throw new NotSupportedException ();
678                         }
679                 }
680
681                 public int WindowLeft {
682                         get {
683                                 if (!inited) {
684                                         Init ();
685                                 }
686
687                                 //GetWindowDimensions ();
688                                 return 0;
689                         }
690                         set {
691                                 if (!inited) {
692                                         Init ();
693                                 }
694
695                                 throw new NotSupportedException ();
696                         }
697                 }
698
699                 public int WindowTop {
700                         get {
701                                 if (!inited) {
702                                         Init ();
703                                 }
704
705                                 //GetWindowDimensions ();
706                                 return 0;
707                         }
708                         set {
709                                 if (!inited) {
710                                         Init ();
711                                 }
712
713                                 throw new NotSupportedException ();
714                         }
715                 }
716
717                 public int WindowWidth {
718                         get {
719                                 if (!inited) {
720                                         Init ();
721                                 }
722
723                                 if (need_window_dimensions)
724                                         GetWindowDimensions ();
725                                 return windowWidth;
726                         }
727                         set {
728                                 if (!inited) {
729                                         Init ();
730                                 }
731
732                                 throw new NotSupportedException ();
733                         }
734                 }
735
736                 public void Clear ()
737                 {
738                         if (!inited) {
739                                 Init ();
740                         }
741
742                         WriteConsole (clear);
743                 }
744
745                 public void Beep (int frequency, int duration)
746                 {
747                         if (!inited) {
748                                 Init ();
749                         }
750
751                         WriteConsole (bell);
752                 }
753
754                 public void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
755                                         int targetLeft, int targetTop, Char sourceChar,
756                                         ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
757                 {
758                         if (!inited) {
759                                 Init ();
760                         }
761
762                         throw new NotImplementedException ();
763                 }
764
765                 void AddToBuffer (int b)
766                 {
767                         if (buffer == null) {
768                                 buffer = new byte [1024];
769                         } else if (writepos >= buffer.Length) {
770                                 byte [] newbuf = new byte [buffer.Length * 2];
771                                 Buffer.BlockCopy (buffer, 0, newbuf, 0, buffer.Length);
772                                 buffer = newbuf;
773                         }
774
775                         buffer [writepos++] = (byte) b;
776                 }
777
778                 void AdjustBuffer ()
779                 {
780                         if (readpos >= writepos) {
781                                 readpos = writepos = 0;
782                         }
783                 }
784
785                 ConsoleKeyInfo CreateKeyInfoFromInt (int n)
786                 {
787                         char c = (char) n;
788                         ConsoleKey key = (ConsoleKey)n;
789                         bool shift = false;
790                         bool ctrl = false;
791                         bool alt = false;
792
793                         if (n == 10) {
794                                 key = ConsoleKey.Enter;
795                         } else if (n == 8 || n == 9 || n == 12 || n == 13 || n == 19) {
796                                 /* Values in ConsoleKey */
797                         } else if (n >= 1 && n <= 26) {
798                                 // For Ctrl-a to Ctrl-z.
799                                 ctrl = true;
800                                 key = ConsoleKey.A + n - 1;
801                         } else if (n == 27) {
802                                 key = ConsoleKey.Escape;
803                         } else if (n >= 'a' && n <= 'z') {
804                                 key = ConsoleKey.A - 'a' + n;
805                         } else if (n >= 'A' && n <= 'Z') {
806                                 shift = true;
807                         } else if (n >= '0' && n <= '9') {
808                         } else {
809                                 key = 0;
810                         }
811
812                         return new ConsoleKeyInfo (c, key, shift, alt, ctrl);
813                 }
814
815                 object GetKeyFromBuffer (bool cooked)
816                 {
817                         if (readpos >= writepos)
818                                 return null;
819
820                         int next = buffer [readpos];
821                         if (!cooked || !rootmap.StartsWith (next)) {
822                                 readpos++;
823                                 AdjustBuffer ();
824                                 return CreateKeyInfoFromInt (next);
825                         }
826
827                         int used;
828                         TermInfoStrings str = rootmap.Match (buffer, readpos, writepos - readpos, out used);
829                         if ((int) str == -1)
830                                 return null;
831
832                         ConsoleKeyInfo key;
833                         if (keymap [str] != null) {
834                                 key = (ConsoleKeyInfo) keymap [str];
835                         } else {
836                                 readpos++;
837                                 AdjustBuffer ();
838                                 return CreateKeyInfoFromInt (next);
839                         }
840
841                         readpos += used;
842                         AdjustBuffer ();
843                         return key;
844                 }
845
846                 ConsoleKeyInfo ReadKeyInternal ()
847                 {
848                         if (!inited) {
849                                 Init ();
850                         }
851
852                         InitKeys ();
853                         object o = null;
854                         while (o == null) {
855                                 o = GetKeyFromBuffer (true);
856                                 if (o == null) {
857                                         if (ConsoleDriver.InternalKeyAvailable (150) > 0) {
858                                                 do {
859                                                         AddToBuffer (stdin.ReadByte ());
860                                                 } while (ConsoleDriver.InternalKeyAvailable (0) > 0);
861                                         } else {
862                                                 o = GetKeyFromBuffer (false);
863                                                 if (o == null)
864                                                         AddToBuffer (stdin.ReadByte ());
865                                         }
866                                 }
867                         }
868
869                         return (ConsoleKeyInfo) o;
870                 }
871
872                 public ConsoleKeyInfo ReadKey (bool intercept)
873                 {
874                         if (!inited)
875                                 Init ();
876                         
877                         ConsoleKeyInfo key = ReadKeyInternal ();
878                         
879                         if (!intercept) {
880                                 // echo the key back to the console
881                                 CStreamWriter writer = Console.stdout as CStreamWriter;
882
883                                 if (writer != null)
884                                         writer.WriteKey (key);
885                                 else
886                                         Console.stdout.Write (key.KeyChar);
887                         }
888                         
889                         return key;
890                 }
891
892                 public string ReadLine ()
893                 {
894                         if (!inited)
895                                 Init ();
896
897                         // Hack to make Iron Python work (since it goes behind our backs
898                         // when writing to the console thus preventing us from keeping
899                         // cursor state normally).
900                         GetCursorPosition ();
901
902                         StringBuilder builder = new StringBuilder ();
903                         bool exit = false;
904                         CStreamWriter writer = Console.stdout as CStreamWriter;
905                         rl_startx = cursorLeft;
906                         rl_starty = cursorTop;
907                         do {
908                                 ConsoleKeyInfo key = ReadKeyInternal ();
909                                 char c = key.KeyChar;
910                                 exit = (c == '\n');
911                                 if (!exit) {
912                                         if (key.Key != ConsoleKey.Backspace)
913                                                 builder.Append (c);
914                                         else if (builder.Length > 0)
915                                                 builder.Length--;
916                                         else {
917                                                 // skips over echoing the key to the console
918                                                 continue;
919                                         }
920                                 }
921                                 
922                                 // echo the key back to the console
923                                 if (writer != null)
924                                         writer.WriteKey (key);
925                                 else
926                                         Console.stdout.Write (c);
927                         } while (!exit);
928                         
929                         rl_startx = -1;
930                         rl_starty = -1;
931                         return builder.ToString ();
932                 }
933
934                 public void ResetColor ()
935                 {
936                         if (!inited) {
937                                 Init ();
938                         }
939
940                         string str = (origPair != null) ? origPair : origColors;
941                         WriteConsole (str);
942                 }
943
944                 public void SetBufferSize (int width, int height)
945                 {
946                         if (!inited) {
947                                 Init ();
948                         }
949
950                         throw new NotImplementedException (String.Empty);
951                 }
952
953                 public void SetCursorPosition (int left, int top)
954                 {
955                         if (!inited) {
956                                 Init ();
957                         }
958
959                         if (bufferWidth == 0 && need_window_dimensions)
960                                 GetWindowDimensions ();
961
962                         if (left < 0 || left >= bufferWidth)
963                                 throw new ArgumentOutOfRangeException ("left", "Value must be positive and below the buffer width.");
964
965                         if (top < 0 || top >= bufferHeight)
966                                 throw new ArgumentOutOfRangeException ("top", "Value must be positive and below the buffer height.");
967
968                         // Either CursorAddress or nothing.
969                         // We might want to play with up/down/left/right/home when ca is not available.
970                         if (cursorAddress == null)
971                                 throw new NotSupportedException ("This terminal does not suport setting the cursor position.");
972
973                         int one = (home_1_1 ? 1 : 0);
974                         WriteConsole (String.Format (cursorAddress, top + one, left + one));
975                         cursorLeft = left;
976                         cursorTop = top;
977                 }
978
979                 public void SetWindowPosition (int left, int top)
980                 {
981                         if (!inited) {
982                                 Init ();
983                         }
984
985                         // No need to throw exceptions here.
986                         //throw new NotSupportedException ();
987                 }
988
989                 public void SetWindowSize (int width, int height)
990                 {
991                         if (!inited) {
992                                 Init ();
993                         }
994
995                         // No need to throw exceptions here.
996                         //throw new NotSupportedException ();
997                 }
998
999
1000                 void CreateKeyMap ()
1001                 {
1002                         keymap = new Hashtable ();
1003                         keymap [TermInfoStrings.KeyBackspace] = new ConsoleKeyInfo ('\0', ConsoleKey.Backspace, false, false, false);
1004                         keymap [TermInfoStrings.KeyClear] = new ConsoleKeyInfo ('\0', ConsoleKey.Clear, false, false, false);
1005                         // Delete character...
1006                         keymap [TermInfoStrings.KeyDown] = new ConsoleKeyInfo ('\0', ConsoleKey.DownArrow, false, false, false);
1007                         keymap [TermInfoStrings.KeyF1] = new ConsoleKeyInfo ('\0', ConsoleKey.F1, false, false, false);
1008                         keymap [TermInfoStrings.KeyF10] = new ConsoleKeyInfo ('\0', ConsoleKey.F10, false, false, false);
1009                         keymap [TermInfoStrings.KeyF2] = new ConsoleKeyInfo ('\0', ConsoleKey.F2, false, false, false);
1010                         keymap [TermInfoStrings.KeyF3] = new ConsoleKeyInfo ('\0', ConsoleKey.F3, false, false, false);
1011                         keymap [TermInfoStrings.KeyF4] = new ConsoleKeyInfo ('\0', ConsoleKey.F4, false, false, false);
1012                         keymap [TermInfoStrings.KeyF5] = new ConsoleKeyInfo ('\0', ConsoleKey.F5, false, false, false);
1013                         keymap [TermInfoStrings.KeyF6] = new ConsoleKeyInfo ('\0', ConsoleKey.F6, false, false, false);
1014                         keymap [TermInfoStrings.KeyF7] = new ConsoleKeyInfo ('\0', ConsoleKey.F7, false, false, false);
1015                         keymap [TermInfoStrings.KeyF8] = new ConsoleKeyInfo ('\0', ConsoleKey.F8, false, false, false);
1016                         keymap [TermInfoStrings.KeyF9] = new ConsoleKeyInfo ('\0', ConsoleKey.F9, false, false, false);
1017                         keymap [TermInfoStrings.KeyHome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
1018
1019                         keymap [TermInfoStrings.KeyLeft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, false, false, false);
1020                         keymap [TermInfoStrings.KeyLl] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
1021                         keymap [TermInfoStrings.KeyNpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
1022                         keymap [TermInfoStrings.KeyPpage] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
1023                         keymap [TermInfoStrings.KeyRight] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, false, false, false);
1024                         keymap [TermInfoStrings.KeySf] = new ConsoleKeyInfo ('\0', ConsoleKey.PageDown, false, false, false);
1025                         keymap [TermInfoStrings.KeySr] = new ConsoleKeyInfo ('\0', ConsoleKey.PageUp, false, false, false);
1026                         keymap [TermInfoStrings.KeyUp] = new ConsoleKeyInfo ('\0', ConsoleKey.UpArrow, false, false, false);
1027                         keymap [TermInfoStrings.KeyA1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad7, false, false, false);
1028                         keymap [TermInfoStrings.KeyA3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad9, false, false, false);
1029                         keymap [TermInfoStrings.KeyB2] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad5, false, false, false);
1030                         keymap [TermInfoStrings.KeyC1] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad1, false, false, false);
1031                         keymap [TermInfoStrings.KeyC3] = new ConsoleKeyInfo ('\0', ConsoleKey.NumPad3, false, false, false);
1032                         keymap [TermInfoStrings.KeyBtab] = new ConsoleKeyInfo ('\0', ConsoleKey.Tab, true, false, false);
1033                         keymap [TermInfoStrings.KeyBeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, false, false, false);
1034                         keymap [TermInfoStrings.KeyCopy] = new ConsoleKeyInfo ('C', ConsoleKey.C, false, true, false);
1035                         keymap [TermInfoStrings.KeyEnd] = new ConsoleKeyInfo ('\0', ConsoleKey.End, false, false, false);
1036                         keymap [TermInfoStrings.KeyEnter] = new ConsoleKeyInfo ('\n', ConsoleKey.Enter, false, false, false);
1037                         keymap [TermInfoStrings.KeyHelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, false, false, false);
1038                         keymap [TermInfoStrings.KeyPrint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, false, false, false);
1039                         keymap [TermInfoStrings.KeyUndo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z , false, true, false);
1040                         keymap [TermInfoStrings.KeySbeg] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1041                         keymap [TermInfoStrings.KeyScopy] = new ConsoleKeyInfo ('C', ConsoleKey.C , true, true, false);
1042                         keymap [TermInfoStrings.KeySdc] = new ConsoleKeyInfo ('\x9', ConsoleKey.Delete, true, false, false);
1043                         keymap [TermInfoStrings.KeyShelp] = new ConsoleKeyInfo ('\0', ConsoleKey.Help, true, false, false);
1044                         keymap [TermInfoStrings.KeyShome] = new ConsoleKeyInfo ('\0', ConsoleKey.Home, true, false, false);
1045                         keymap [TermInfoStrings.KeySleft] = new ConsoleKeyInfo ('\0', ConsoleKey.LeftArrow, true, false, false);
1046                         keymap [TermInfoStrings.KeySprint] = new ConsoleKeyInfo ('\0', ConsoleKey.Print, true, false, false);
1047                         keymap [TermInfoStrings.KeySright] = new ConsoleKeyInfo ('\0', ConsoleKey.RightArrow, true, false, false);
1048                         keymap [TermInfoStrings.KeySundo] = new ConsoleKeyInfo ('Z', ConsoleKey.Z, true, false, false);
1049                         keymap [TermInfoStrings.KeyF11] = new ConsoleKeyInfo ('\0', ConsoleKey.F11, false, false, false);
1050                         keymap [TermInfoStrings.KeyF12] = new ConsoleKeyInfo ('\0', ConsoleKey.F12 , false, false, false);
1051                         keymap [TermInfoStrings.KeyF13] = new ConsoleKeyInfo ('\0', ConsoleKey.F13, false, false, false);
1052                         keymap [TermInfoStrings.KeyF14] = new ConsoleKeyInfo ('\0', ConsoleKey.F14, false, false, false);
1053                         keymap [TermInfoStrings.KeyF15] = new ConsoleKeyInfo ('\0', ConsoleKey.F15, false, false, false);
1054                         keymap [TermInfoStrings.KeyF16] = new ConsoleKeyInfo ('\0', ConsoleKey.F16, false, false, false);
1055                         keymap [TermInfoStrings.KeyF17] = new ConsoleKeyInfo ('\0', ConsoleKey.F17, false, false, false);
1056                         keymap [TermInfoStrings.KeyF18] = new ConsoleKeyInfo ('\0', ConsoleKey.F18, false, false, false);
1057                         keymap [TermInfoStrings.KeyF19] = new ConsoleKeyInfo ('\0', ConsoleKey.F19, false, false, false);
1058                         keymap [TermInfoStrings.KeyF20] = new ConsoleKeyInfo ('\0', ConsoleKey.F20, false, false, false);
1059                         keymap [TermInfoStrings.KeyF21] = new ConsoleKeyInfo ('\0', ConsoleKey.F21, false, false, false);
1060                         keymap [TermInfoStrings.KeyF22] = new ConsoleKeyInfo ('\0', ConsoleKey.F22, false, false, false);
1061                         keymap [TermInfoStrings.KeyF23] = new ConsoleKeyInfo ('\0', ConsoleKey.F23, false, false, false);
1062                         keymap [TermInfoStrings.KeyF24] = new ConsoleKeyInfo ('\0', ConsoleKey.F24, false, false, false);
1063                 }
1064
1065                 void InitKeys ()
1066                 {
1067                         if (initKeys)
1068                                 return;
1069
1070                         CreateKeyMap ();
1071                         rootmap = new ByteMatcher ();
1072                         AddStringMapping (TermInfoStrings.KeyBackspace);
1073                         AddStringMapping (TermInfoStrings.KeyClear);
1074                         AddStringMapping (TermInfoStrings.KeyDown);
1075                         AddStringMapping (TermInfoStrings.KeyF1);
1076                         AddStringMapping (TermInfoStrings.KeyF10);
1077                         AddStringMapping (TermInfoStrings.KeyF2);
1078                         AddStringMapping (TermInfoStrings.KeyF3);
1079                         AddStringMapping (TermInfoStrings.KeyF4);
1080                         AddStringMapping (TermInfoStrings.KeyF5);
1081                         AddStringMapping (TermInfoStrings.KeyF6);
1082                         AddStringMapping (TermInfoStrings.KeyF7);
1083                         AddStringMapping (TermInfoStrings.KeyF8);
1084                         AddStringMapping (TermInfoStrings.KeyF9);
1085                         AddStringMapping (TermInfoStrings.KeyHome);
1086                         AddStringMapping (TermInfoStrings.KeyLeft);
1087                         AddStringMapping (TermInfoStrings.KeyLl);
1088                         AddStringMapping (TermInfoStrings.KeyNpage);
1089                         AddStringMapping (TermInfoStrings.KeyPpage);
1090                         AddStringMapping (TermInfoStrings.KeyRight);
1091                         AddStringMapping (TermInfoStrings.KeySf);
1092                         AddStringMapping (TermInfoStrings.KeySr);
1093                         AddStringMapping (TermInfoStrings.KeyUp);
1094                         AddStringMapping (TermInfoStrings.KeyA1);
1095                         AddStringMapping (TermInfoStrings.KeyA3);
1096                         AddStringMapping (TermInfoStrings.KeyB2);
1097                         AddStringMapping (TermInfoStrings.KeyC1);
1098                         AddStringMapping (TermInfoStrings.KeyC3);
1099                         AddStringMapping (TermInfoStrings.KeyBtab);
1100                         AddStringMapping (TermInfoStrings.KeyBeg);
1101                         AddStringMapping (TermInfoStrings.KeyCopy);
1102                         AddStringMapping (TermInfoStrings.KeyEnd);
1103                         AddStringMapping (TermInfoStrings.KeyEnter);
1104                         AddStringMapping (TermInfoStrings.KeyHelp);
1105                         AddStringMapping (TermInfoStrings.KeyPrint);
1106                         AddStringMapping (TermInfoStrings.KeyUndo);
1107                         AddStringMapping (TermInfoStrings.KeySbeg);
1108                         AddStringMapping (TermInfoStrings.KeyScopy);
1109                         AddStringMapping (TermInfoStrings.KeySdc);
1110                         AddStringMapping (TermInfoStrings.KeyShelp);
1111                         AddStringMapping (TermInfoStrings.KeyShome);
1112                         AddStringMapping (TermInfoStrings.KeySleft);
1113                         AddStringMapping (TermInfoStrings.KeySprint);
1114                         AddStringMapping (TermInfoStrings.KeySright);
1115                         AddStringMapping (TermInfoStrings.KeySundo);
1116                         AddStringMapping (TermInfoStrings.KeyF11);
1117                         AddStringMapping (TermInfoStrings.KeyF12);
1118                         AddStringMapping (TermInfoStrings.KeyF13);
1119                         AddStringMapping (TermInfoStrings.KeyF14);
1120                         AddStringMapping (TermInfoStrings.KeyF15);
1121                         AddStringMapping (TermInfoStrings.KeyF16);
1122                         AddStringMapping (TermInfoStrings.KeyF17);
1123                         AddStringMapping (TermInfoStrings.KeyF18);
1124                         AddStringMapping (TermInfoStrings.KeyF19);
1125                         AddStringMapping (TermInfoStrings.KeyF20);
1126                         AddStringMapping (TermInfoStrings.KeyF21);
1127                         AddStringMapping (TermInfoStrings.KeyF22);
1128                         AddStringMapping (TermInfoStrings.KeyF23);
1129                         AddStringMapping (TermInfoStrings.KeyF24);
1130                         rootmap.Sort ();
1131                         initKeys = true;
1132                 }
1133
1134                 void AddStringMapping (TermInfoStrings s)
1135                 {
1136                         byte [] bytes = reader.GetStringBytes (s);
1137                         if (bytes == null)
1138                                 return;
1139
1140                         rootmap.AddMapping (s, bytes);
1141                 }
1142         }
1143
1144         class ByteMatcher {
1145                 Hashtable map = new Hashtable ();
1146                 Hashtable starts = new Hashtable ();
1147
1148                 public void AddMapping (TermInfoStrings key, byte [] val)
1149                 {
1150                         if (val.Length == 0)
1151                                 return;
1152
1153                         map [val] = key;
1154                         starts [(int) val [0]] = true;
1155                 }
1156
1157                 public void Sort ()
1158                 {
1159                 }
1160
1161                 public bool StartsWith (int c)
1162                 {
1163                         return (starts [c] != null);
1164                 }
1165
1166                 public TermInfoStrings Match (byte [] buffer, int offset, int length, out int used)
1167                 {
1168                         foreach (byte [] bytes in map.Keys) {
1169                                 for (int i = 0; i < bytes.Length && i < length; i++) {
1170                                         if (bytes [i] != buffer [offset + i])
1171                                                 break;
1172
1173                                         if (bytes.Length - 1 == i) {
1174                                                 used = bytes.Length;
1175                                                 return (TermInfoStrings) map [bytes];
1176                                         }
1177                                 }
1178                         }
1179
1180                         used = 0;
1181                         return (TermInfoStrings) (-1);
1182                 }
1183         }
1184 }
1185 #endif
1186