csharp: support command line arguments for scripts
[mono.git] / mcs / tools / csharp / repl.cs
1 //
2 // repl.cs: Support for using the compiler in interactive mode (read-eval-print loop)
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnome.org)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
11 // Copyright 2011-2013 Xamarin Inc
12 //
13 //
14 // TODO:
15 //   Do not print results in Evaluate, do that elsewhere in preparation for Eval refactoring.
16 //   Driver.PartialReset should not reset the coretypes, nor the optional types, to avoid
17 //      computing that on every call.
18 //
19 using System;
20 using System.IO;
21 using System.Text;
22 using System.Globalization;
23 using System.Collections;
24 using System.Reflection;
25 using System.Reflection.Emit;
26 using System.Threading;
27 using System.Net;
28 using System.Net.Sockets;
29 using System.Collections.Generic;
30
31 using Mono.CSharp;
32
33 namespace Mono {
34
35         public class Driver {
36                 public static string StartupEvalExpression;
37                 static int? attach;
38                 static string target_host;
39                 static int target_port;
40                 static string agent;
41                 static string [] script_args;
42
43                 public static string [] ScriptArgs => script_args;
44                 
45                 static int Main (string [] args)
46                 {
47                         if (!SplitDriverAndScriptArguments (ref args, out script_args))
48                                 return 1;
49
50                         var cmd = new CommandLineParser (Console.Out);
51                         cmd.UnknownOptionHandler += HandleExtraArguments;
52
53                         // Enable unsafe code by default
54                         var settings = new CompilerSettings () {
55                                 Unsafe = true
56                         };
57
58                         if (!cmd.ParseArguments (settings, args))
59                                 return 1;
60
61                         var startup_files = new string [settings.SourceFiles.Count];
62                         int i = 0;
63                         foreach (var source in settings.SourceFiles)
64                                 startup_files [i++] = source.OriginalFullPathName;
65                         settings.SourceFiles.Clear ();
66
67                         TextWriter agent_stderr = null;
68                         ReportPrinter printer;
69                         if (agent != null) {
70                                 agent_stderr = new StringWriter ();
71                                 printer = new StreamReportPrinter (agent_stderr);
72                         } else {
73                                 printer = new ConsoleReportPrinter ();
74                         }
75
76                         var eval = new Evaluator (new CompilerContext (settings, printer));
77
78                         eval.InteractiveBaseClass = typeof (InteractiveBaseShell);
79                         eval.DescribeTypeExpressions = true;
80                         eval.WaitOnTask = true;
81
82                         CSharpShell shell;
83 #if !ON_DOTNET
84                         if (attach.HasValue) {
85                                 shell = new ClientCSharpShell_v1 (eval, attach.Value);
86                         } else if (agent != null) {
87                                 new CSharpAgent (eval, agent, agent_stderr).Run (startup_files);
88                                 return 0;
89                         } else
90 #endif
91                         if (target_host != null) 
92                                 shell = new ClientCSharpShell  (eval, target_host, target_port);
93                         else 
94                                 shell = new CSharpShell (eval);
95
96                         return shell.Run (startup_files);
97                 }
98
99                 static bool SplitDriverAndScriptArguments (ref string [] driver_args, out string [] script_args)
100                 {
101                         // split command line arguments into two groups:
102                         // - anything before '--' or '-s' goes to the mcs driver, which may
103                         //   call back into the csharp driver for further processing
104                         // - anything after '--' or '-s' is made available to the REPL/script
105                         //   via the 'Args' global, similar to csi.
106                         // - if '-s' is used, the argument immediately following it will
107                         //   also be processed by the mcs driver (e.g. a source file)
108
109                         int driver_args_count = 0;
110                         int script_args_offset = 0;
111                         string script_file = null;
112
113                         while (driver_args_count < driver_args.Length && script_args_offset == 0) {
114                                 switch (driver_args [driver_args_count]) {
115                                 case "--":
116                                         script_args_offset = driver_args_count + 1;
117                                         break;
118                                 case "-s":
119                                         if (driver_args_count + 1 >= driver_args.Length) {
120                                                 script_args = null;
121                                                 Console.Error.WriteLine ("usage is: -s SCRIPT_FILE");
122                                                 return false;
123                                         }
124                                         driver_args_count++;
125                                         script_file = driver_args [driver_args_count];
126                                         script_args_offset = driver_args_count + 1;
127                                         break;
128                                 default:
129                                         driver_args_count++;
130                                         break;
131                                 }
132                         }
133
134                         if (script_args_offset > 0) {
135                                 int script_args_count = driver_args.Length - script_args_offset;
136                                 script_args = new string [script_args_count];
137                                 Array.Copy (driver_args, script_args_offset, script_args, 0, script_args_count);
138                         } else
139                                 script_args = Array.Empty<string> ();
140
141                         Array.Resize (ref driver_args, driver_args_count);
142                         if (script_file != null)
143                                 driver_args [driver_args_count - 1] = script_file;
144
145                         return true;
146                 }
147
148                 static int HandleExtraArguments (string [] args, int pos)
149                 {
150                         switch (args [pos]) {
151                         case "-e":
152                                 if (pos + 1 < args.Length) {
153                                         StartupEvalExpression = args[pos + 1];
154                                         return pos + 1;
155                                 }
156                                 break;
157                         case "--attach":
158                                 if (pos + 1 < args.Length) {
159                                         attach = Int32.Parse (args[1]);
160                                         return pos + 1;
161                                 }
162                                 break;
163                         default:
164                                 if (args [pos].StartsWith ("--server=")){
165                                         var hostport = args [pos].Substring (9);
166                                         int p = hostport.IndexOf (':');
167                                         if (p == -1){
168                                                 target_host = hostport;
169                                                 target_port = 10000;
170                                         } else {
171                                                 target_host = hostport.Substring (0,p);
172                                                 if (!int.TryParse (hostport.Substring (p), out target_port)){
173                                                         Console.Error.WriteLine ("Usage is: --server[=host[:port]");
174                                                         Environment.Exit (1);
175                                                 }
176                                         }
177                                         return pos + 1;
178                                 }
179                                 if (args [pos].StartsWith ("--client")){
180                                         target_host = "localhost";
181                                         target_port = 10000;
182                                         return pos + 1;
183                                 }
184                                 if (args [pos].StartsWith ("--agent:")) {
185                                         agent = args[pos];
186                                         return pos + 1;
187                                 } else {
188                                         return -1;
189                                 }
190                         }
191                         return -1;
192                 }
193                 
194         }
195
196         public class InteractiveBaseShell : InteractiveBase {
197                 static bool tab_at_start_completes;
198                 
199                 static InteractiveBaseShell ()
200                 {
201                         tab_at_start_completes = false;
202                 }
203
204                 internal static Mono.Terminal.LineEditor Editor;
205                 
206                 public static bool TabAtStartCompletes {
207                         get {
208                                 return tab_at_start_completes;
209                         }
210
211                         set {
212                                 tab_at_start_completes = value;
213                                 if (Editor != null)
214                                         Editor.TabAtStartCompletes = value;
215                         }
216                 }
217
218                 public static new string help {
219                         get {
220                                 return InteractiveBase.help +
221                                         "  TabAtStartCompletes      - Whether tab will complete even on empty lines\n" +
222                                         "  Args                     - Any command line arguments passed to csharp\n" +
223                                         "                             after the '--' (stop processing) argument";
224                         }
225                 }
226
227                 public static string [] Args => Driver.ScriptArgs;
228         }
229         
230         public class CSharpShell {
231                 static bool isatty = true, is_unix = false;
232                 protected string [] startup_files;
233                 
234                 Mono.Terminal.LineEditor editor;
235                 bool dumb;
236                 readonly Evaluator evaluator;
237
238                 public CSharpShell (Evaluator evaluator)
239                 {
240                         this.evaluator = evaluator;
241                 }
242
243                 protected virtual void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
244                 {
245                         // Do not about our program
246                         a.Cancel = true;
247
248                         evaluator.Interrupt ();
249                 }
250                 
251                 void SetupConsole ()
252                 {
253                         if (is_unix){
254                                 string term = Environment.GetEnvironmentVariable ("TERM");
255                                 dumb = term == "dumb" || term == null || isatty == false;
256                         } else
257                                 dumb = false;
258                         
259                         editor = new Mono.Terminal.LineEditor ("csharp", 300) {
260                                 HeuristicsMode = "csharp"
261                         };
262                         InteractiveBaseShell.Editor = editor;
263
264                         editor.AutoCompleteEvent += delegate (string s, int pos){
265                                 string prefix = null;
266
267                                 string complete = s.Substring (0, pos);
268                                 
269                                 string [] completions = evaluator.GetCompletions (complete, out prefix);
270                                 
271                                 return new Mono.Terminal.LineEditor.Completion (prefix, completions);
272                         };
273                         
274 #if false
275                         //
276                         // This is a sample of how completions sould be implemented.
277                         //
278                         editor.AutoCompleteEvent += delegate (string s, int pos){
279
280                                 // Single match: "Substring": Sub-string
281                                 if (s.EndsWith ("Sub")){
282                                         return new string [] { "string" };
283                                 }
284
285                                 // Multiple matches: "ToString" and "ToLower"
286                                 if (s.EndsWith ("T")){
287                                         return new string [] { "ToString", "ToLower" };
288                                 }
289                                 return null;
290                         };
291 #endif
292                         
293                         Console.CancelKeyPress += ConsoleInterrupt;
294                 }
295
296                 string GetLine (bool primary)
297                 {
298                         string prompt = primary ? InteractiveBase.Prompt : InteractiveBase.ContinuationPrompt;
299
300                         if (dumb){
301                                 if (isatty)
302                                         Console.Write (prompt);
303
304                                 return Console.ReadLine ();
305                         } else {
306                                 return editor.Edit (prompt, "");
307                         }
308                 }
309
310                 delegate string ReadLiner (bool primary);
311
312                 void InitializeUsing ()
313                 {
314                         Evaluate ("using System; using System.Linq; using System.Collections.Generic; using System.Collections;");
315                 }
316
317                 void InitTerminal (bool show_banner)
318                 {
319                         int p = (int) Environment.OSVersion.Platform;
320                         is_unix = (p == 4) || (p == 128);
321
322                         isatty = !Console.IsInputRedirected && !Console.IsOutputRedirected;
323
324                         // Work around, since Console is not accounting for
325                         // cursor position when writing to Stderr.  It also
326                         // has the undesirable side effect of making
327                         // errors plain, with no coloring.
328 //                      Report.Stderr = Console.Out;
329                         SetupConsole ();
330
331                         if (isatty && show_banner)
332                                 Console.WriteLine ("Mono C# Shell, type \"help;\" for help\n\nEnter statements below.");
333
334                 }
335
336                 void ExecuteSources (IEnumerable<string> sources, bool ignore_errors)
337                 {
338                         foreach (string file in sources){
339                                 try {
340                                         try {
341                                                 bool first = true;
342                         
343                                                 using (System.IO.StreamReader r = System.IO.File.OpenText (file)){
344                                                         ReadEvalPrintLoopWith (p => {
345                                                                 var line = r.ReadLine ();
346                                                                 if (first){
347                                                                         if (line.StartsWith ("#!"))
348                                                                                 line = r.ReadLine ();
349                                                                         first = false;
350                                                                 }
351                                                                 return line;
352                                                         });
353                                                 }
354                                         } catch (FileNotFoundException){
355                                                 Console.Error.WriteLine ("cs2001: Source file `{0}' not found", file);
356                                                 return;
357                                         }
358                                 } catch {
359                                         if (!ignore_errors)
360                                                 throw;
361                                 }
362                         }
363                 }
364                 
365                 protected virtual void LoadStartupFiles ()
366                 {
367                         string dir = Path.Combine (
368                                 Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
369                                 "csharp");
370                         if (!Directory.Exists (dir))
371                                 return;
372
373                         List<string> sources = new List<string> ();
374                         List<string> libraries = new List<string> ();
375                         
376                         foreach (string file in System.IO.Directory.GetFiles (dir)){
377                                 string l = file.ToLower ();
378                                 
379                                 if (l.EndsWith (".cs"))
380                                         sources.Add (file);
381                                 else if (l.EndsWith (".dll"))
382                                         libraries.Add (file);
383                         }
384
385                         foreach (string file in libraries)
386                                 evaluator.LoadAssembly (file);
387
388                         ExecuteSources (sources, true);
389                 }
390
391                 void ReadEvalPrintLoopWith (ReadLiner readline)
392                 {
393                         string expr = null;
394                         while (!InteractiveBase.QuitRequested){
395                                 string input = readline (expr == null);
396                                 if (input == null)
397                                         return;
398
399                                 if (input == "")
400                                         continue;
401
402                                 expr = expr == null ? input : expr + "\n" + input;
403                                 
404                                 expr = Evaluate (expr);
405                         }
406                 }
407
408                 public int ReadEvalPrintLoop ()
409                 {
410                         if (startup_files != null && startup_files.Length == 0)
411                                 InitTerminal (startup_files.Length == 0 && Driver.StartupEvalExpression == null);
412
413                         InitializeUsing ();
414
415                         LoadStartupFiles ();
416
417                         if (startup_files != null && startup_files.Length != 0) {
418                                 ExecuteSources (startup_files, false);
419                         } else {
420                                 if (Driver.StartupEvalExpression != null){
421                                         ReadEvalPrintLoopWith (p => {
422                                                 var ret = Driver.StartupEvalExpression;
423                                                 Driver.StartupEvalExpression = null;
424                                                 return ret;
425                                                 });
426                                 } else {
427                                         ReadEvalPrintLoopWith (GetLine);
428                                 }
429                                 
430                                 editor.SaveHistory ();
431                         }
432
433                         Console.CancelKeyPress -= ConsoleInterrupt;
434                         
435                         return 0;
436                 }
437
438                 protected virtual string Evaluate (string input)
439                 {
440                         bool result_set;
441                         object result;
442
443                         try {
444                                 input = evaluator.Evaluate (input, out result, out result_set);
445
446                                 if (result_set){
447                                         PrettyPrint (Console.Out, result);
448                                         Console.WriteLine ();
449                                 }
450                         } catch (Exception e){
451                                 Console.WriteLine (e);
452                                 return null;
453                         }
454                         
455                         return input;
456                 }
457
458                 static void p (TextWriter output, string s)
459                 {
460                         output.Write (s);
461                 }
462
463                 static void EscapeString (TextWriter output, string s)
464                 {
465                         foreach (var c in s){
466                                 if (c >= 32){
467                                         output.Write (c);
468                                         continue;
469                                 }
470                                 switch (c){
471                                 case '\"':
472                                         output.Write ("\\\""); break;
473                                 case '\a':
474                                         output.Write ("\\a"); break;
475                                 case '\b':
476                                         output.Write ("\\b"); break;
477                                 case '\n':
478                                         output.Write ("\n");
479                                         break;
480                                 
481                                 case '\v':
482                                         output.Write ("\\v");
483                                         break;
484                                 
485                                 case '\r':
486                                         output.Write ("\\r");
487                                         break;
488                                 
489                                 case '\f':
490                                         output.Write ("\\f");
491                                         break;
492                                 
493                                 case '\t':
494                                         output.Write ("\\t");
495                                         break;
496
497                                 default:
498                                         output.Write ("\\x{0:x}", (int) c);
499                                         break;
500                                 }
501                         }
502                 }
503                 
504                 static void EscapeChar (TextWriter output, char c)
505                 {
506                         if (c == '\''){
507                                 output.Write ("'\\''");
508                                 return;
509                         }
510                         if (c >= 32){
511                                 output.Write ("'{0}'", c);
512                                 return;
513                         }
514                         switch (c){
515                         case '\a':
516                                 output.Write ("'\\a'");
517                                 break;
518
519                         case '\b':
520                                 output.Write ("'\\b'");
521                                 break;
522                                 
523                         case '\n':
524                                 output.Write ("'\\n'");
525                                 break;
526                                 
527                         case '\v':
528                                 output.Write ("'\\v'");
529                                 break;
530                                 
531                         case '\r':
532                                 output.Write ("'\\r'");
533                                 break;
534                                 
535                         case '\f':
536                                 output.Write ("'\\f'");
537                                 break;
538                                 
539                         case '\t':
540                                 output.Write ("'\\t");
541                                 break;
542
543                         default:
544                                 output.Write ("'\\x{0:x}'", (int) c);
545                                 break;
546                         }
547                 }
548
549                 // Some types (System.Json.JsonPrimitive) implement
550                 // IEnumerator and yet, throw an exception when we
551                 // try to use them, helper function to check for that
552                 // condition
553                 static internal bool WorksAsEnumerable (object obj)
554                 {
555                         IEnumerable enumerable = obj as IEnumerable;
556                         if (enumerable != null){
557                                 try {
558                                         enumerable.GetEnumerator ();
559                                         return true;
560                                 } catch {
561                                         // nothing, we return false below
562                                 }
563                         }
564                         return false;
565                 }
566                 
567                 internal static void PrettyPrint (TextWriter output, object result)
568                 {
569                         if (result == null){
570                                 p (output, "null");
571                                 return;
572                         }
573                         
574                         if (result is Array){
575                                 Array a = (Array) result;
576                                 
577                                 p (output, "{ ");
578                                 int top = a.GetUpperBound (0);
579                                 for (int i = a.GetLowerBound (0); i <= top; i++){
580                                         PrettyPrint (output, a.GetValue (i));
581                                         if (i != top)
582                                                 p (output, ", ");
583                                 }
584                                 p (output, " }");
585                         } else if (result is bool){
586                                 if ((bool) result)
587                                         p (output, "true");
588                                 else
589                                         p (output, "false");
590                         } else if (result is string){
591                                 p (output, "\"");
592                                 EscapeString (output, (string)result);
593                                 p (output, "\"");
594                         } else if (result is IDictionary){
595                                 IDictionary dict = (IDictionary) result;
596                                 int top = dict.Count, count = 0;
597                                 
598                                 p (output, "{");
599                                 foreach (DictionaryEntry entry in dict){
600                                         count++;
601                                         p (output, "{ ");
602                                         PrettyPrint (output, entry.Key);
603                                         p (output, ", ");
604                                         PrettyPrint (output, entry.Value);
605                                         if (count != top)
606                                                 p (output, " }, ");
607                                         else
608                                                 p (output, " }");
609                                 }
610                                 p (output, "}");
611                         } else if (WorksAsEnumerable (result)) {
612                                 int i = 0;
613                                 p (output, "{ ");
614                                 foreach (object item in (IEnumerable) result) {
615                                         if (i++ != 0)
616                                                 p (output, ", ");
617
618                                         PrettyPrint (output, item);
619                                 }
620                                 p (output, " }");
621                         } else if (result is char) {
622                                 EscapeChar (output, (char) result);
623                         } else {
624                                 p (output, result.ToString ());
625                         }
626                 }
627
628                 public virtual int Run (string [] startup_files)
629                 {
630                         this.startup_files = startup_files;
631                         return ReadEvalPrintLoop ();
632                 }
633                 
634         }
635
636         //
637         // Stream helper extension methods
638         //
639         public static class StreamHelper {
640                 static DataConverter converter = DataConverter.LittleEndian;
641                 
642                 static void GetBuffer (this Stream stream, byte [] b)
643                 {
644                         int n, offset = 0;
645                         int len = b.Length;
646
647                         do {
648                                 n = stream.Read (b, offset, len);
649                                 if (n == 0)
650                                         throw new IOException ("End reached");
651
652                                 offset += n;
653                                 len -= n;
654                         } while (len > 0);
655                 }
656
657                 public static int GetInt (this Stream stream)
658                 {
659                         byte [] b = new byte [4];
660                         stream.GetBuffer (b);
661                         return converter.GetInt32 (b, 0);
662                 }
663
664                 public static string GetString (this Stream stream)
665                 {
666                         int len = stream.GetInt ();
667                         if (len == 0)
668                                 return "";
669
670                         byte [] b = new byte [len];
671                         stream.GetBuffer (b);
672
673                         return Encoding.UTF8.GetString (b);
674                 }
675
676                 public static void WriteInt (this Stream stream, int n)
677                 {
678                         byte [] bytes = converter.GetBytes (n);
679                         stream.Write (bytes, 0, bytes.Length);
680                 }
681         
682                 public static void WriteString (this Stream stream, string s)
683                 {
684                         stream.WriteInt (s.Length);
685                         byte [] bytes = Encoding.UTF8.GetBytes (s);
686                         stream.Write (bytes, 0, bytes.Length);
687                 }
688         }
689         
690         public enum AgentStatus : byte {
691                 // Received partial input, complete
692                 PARTIAL_INPUT  = 1,
693         
694                 // The result was set, expect the string with the result
695                 RESULT_SET     = 2,
696         
697                 // No result was set, complete
698                 RESULT_NOT_SET = 3,
699         
700                 // Errors and warnings string follows
701                 ERROR          = 4,
702
703                 // Stdout
704                 STDOUT         = 5,
705         }
706
707         class ClientCSharpShell : CSharpShell {
708                 string target_host;
709                 int target_port;
710                 
711                 public ClientCSharpShell (Evaluator evaluator, string target_host, int target_port) : base (evaluator)
712                 {
713                         this.target_port = target_port;
714                         this.target_host = target_host;
715                 }
716
717                 T ConnectServer<T> (Func<NetworkStream,T> callback, Action<Exception> error)
718                 {
719                         try {
720                                 var client = new TcpClient (target_host, target_port);
721                                 var ns = client.GetStream ();
722                                 T ret = callback (ns);
723                                 ns.Flush ();
724                                 ns.Close ();
725                                 client.Close ();
726                                 return ret;
727                         } catch (Exception e){
728                                 error (e);
729                                 return default(T);
730                         }
731                 }
732                 
733                 protected override string Evaluate (string input)
734                 {
735                         return ConnectServer<string> ((ns)=> {
736                                 try {
737                                         ns.WriteString ("EVALTXT");
738                                         ns.WriteString (input);
739
740                                         while (true) {
741                                                 AgentStatus s = (AgentStatus) ns.ReadByte ();
742                                         
743                                                 switch (s){
744                                                 case AgentStatus.PARTIAL_INPUT:
745                                                         return input;
746                                                 
747                                                 case AgentStatus.ERROR:
748                                                         string err = ns.GetString ();
749                                                         Console.Error.WriteLine (err);
750                                                         break;
751
752                                                 case AgentStatus.STDOUT:
753                                                         string stdout = ns.GetString ();
754                                                         Console.WriteLine (stdout);
755                                                         break;
756                                                 
757                                                 case AgentStatus.RESULT_NOT_SET:
758                                                         return null;
759                                                 
760                                                 case AgentStatus.RESULT_SET:
761                                                         string res = ns.GetString ();
762                                                         Console.WriteLine (res);
763                                                         return null;
764                                                 }
765                                         }
766                                 } catch (Exception e){
767                                         Console.Error.WriteLine ("Error evaluating expression, exception: {0}", e);
768                                 }
769                                 return null;
770                         }, (e) => {
771                                 Console.Error.WriteLine ("Error communicating with server {0}", e);
772                         });
773                 }
774                 
775                 public override int Run (string [] startup_files)
776                 {
777                         // The difference is that we do not call Evaluator.Init, that is done on the target
778                         this.startup_files = startup_files;
779                         return ReadEvalPrintLoop ();
780                 }
781         
782                 protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
783                 {
784                         ConnectServer<int> ((ns)=> {
785                                 ns.WriteString ("INTERRUPT");
786                                 return 0;
787                         }, (e) => { });
788                 }
789                         
790         }
791
792 #if !ON_DOTNET
793         //
794         // A shell connected to a CSharpAgent running in a remote process.
795         //  - maybe add 'class_name' and 'method_name' arguments to LoadAgent.
796         //  - Support Gtk and Winforms main loops if detected, this should
797         //    probably be done as a separate agent in a separate place.
798         //
799         class ClientCSharpShell_v1 : CSharpShell {
800                 NetworkStream ns, interrupt_stream;
801                 
802                 public ClientCSharpShell_v1 (Evaluator evaluator, int pid)
803                         : base (evaluator)
804                 {
805                         // Create a server socket we listen on whose address is passed to the agent
806                         TcpListener listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
807                         listener.Start ();
808                         TcpListener interrupt_listener = new TcpListener (new IPEndPoint (IPAddress.Loopback, 0));
809                         interrupt_listener.Start ();
810         
811                         string agent_assembly = typeof (ClientCSharpShell).Assembly.Location;
812                         string agent_arg = String.Format ("--agent:{0}:{1}" ,
813                                                           ((IPEndPoint)listener.Server.LocalEndPoint).Port,
814                                                           ((IPEndPoint)interrupt_listener.Server.LocalEndPoint).Port);
815         
816                         var vm = new Attach.VirtualMachine (pid);
817                         vm.Attach (agent_assembly, agent_arg);
818         
819                         /* Wait for the client to connect */
820                         TcpClient client = listener.AcceptTcpClient ();
821                         ns = client.GetStream ();
822                         TcpClient interrupt_client = interrupt_listener.AcceptTcpClient ();
823                         interrupt_stream = interrupt_client.GetStream ();
824         
825                         Console.WriteLine ("Connected.");
826                 }
827
828                 //
829                 // A remote version of Evaluate
830                 //
831                 protected override string Evaluate (string input)
832                 {
833                         ns.WriteString (input);
834                         while (true) {
835                                 AgentStatus s = (AgentStatus) ns.ReadByte ();
836         
837                                 switch (s){
838                                 case AgentStatus.PARTIAL_INPUT:
839                                         return input;
840         
841                                 case AgentStatus.ERROR:
842                                         string err = ns.GetString ();
843                                         Console.Error.WriteLine (err);
844                                         break;
845         
846                                 case AgentStatus.RESULT_NOT_SET:
847                                         return null;
848         
849                                 case AgentStatus.RESULT_SET:
850                                         string res = ns.GetString ();
851                                         Console.WriteLine (res);
852                                         return null;
853                                 }
854                         }
855                 }
856                 
857                 public override int Run (string [] startup_files)
858                 {
859                         // The difference is that we do not call Evaluator.Init, that is done on the target
860                         this.startup_files = startup_files;
861                         return ReadEvalPrintLoop ();
862                 }
863         
864                 protected override void ConsoleInterrupt (object sender, ConsoleCancelEventArgs a)
865                 {
866                         // Do not about our program
867                         a.Cancel = true;
868         
869                         interrupt_stream.WriteByte (0);
870                         int c = interrupt_stream.ReadByte ();
871                         if (c != -1)
872                                 Console.WriteLine ("Execution interrupted");
873                 }
874                         
875         }
876
877         //
878         // This is the agent loaded into the target process when using --attach.
879         //
880         class CSharpAgent
881         {
882                 NetworkStream interrupt_stream;
883                 readonly Evaluator evaluator;
884                 TextWriter stderr;
885                 
886                 public CSharpAgent (Evaluator evaluator, String arg, TextWriter stderr)
887                 {
888                         this.evaluator = evaluator;
889                         this.stderr = stderr;
890                         new Thread (new ParameterizedThreadStart (Run)).Start (arg);
891                 }
892
893                 public void InterruptListener ()
894                 {
895                         while (true){
896                                 int b = interrupt_stream.ReadByte();
897                                 if (b == -1)
898                                         return;
899                                 evaluator.Interrupt ();
900                                 interrupt_stream.WriteByte (0);
901                         }
902                 }
903                 
904                 public void Run (object o)
905                 {
906                         string arg = (string)o;
907                         string ports = arg.Substring (8);
908                         int sp = ports.IndexOf (':');
909                         int port = Int32.Parse (ports.Substring (0, sp));
910                         int interrupt_port = Int32.Parse (ports.Substring (sp+1));
911         
912                         Console.WriteLine ("csharp-agent: started, connecting to localhost:" + port);
913         
914                         TcpClient client = new TcpClient ("127.0.0.1", port);
915                         TcpClient interrupt_client = new TcpClient ("127.0.0.1", interrupt_port);
916                         Console.WriteLine ("csharp-agent: connected.");
917         
918                         NetworkStream s = client.GetStream ();
919                         interrupt_stream = interrupt_client.GetStream ();
920                         new Thread (InterruptListener).Start ();
921
922                         try {
923                                 // Add all assemblies loaded later
924                                 AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
925         
926                                 // Add all currently loaded assemblies
927                                 foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) {
928                                         // Some assemblies seem to be already loaded, and loading them again causes 'defined multiple times' errors
929                                         if (a.GetName ().Name != "mscorlib" && a.GetName ().Name != "System.Core" && a.GetName ().Name != "System")
930                                                 evaluator.ReferenceAssembly (a);
931                                 }
932         
933                                 RunRepl (s);
934                         } finally {
935                                 AppDomain.CurrentDomain.AssemblyLoad -= AssemblyLoaded;
936                                 client.Close ();
937                                 interrupt_client.Close ();
938                                 Console.WriteLine ("csharp-agent: disconnected.");                      
939                         }
940                 }
941         
942                 void AssemblyLoaded (object sender, AssemblyLoadEventArgs e)
943                 {
944                         evaluator.ReferenceAssembly (e.LoadedAssembly);
945                 }
946         
947                 public void RunRepl (NetworkStream s)
948                 {
949                         string input = null;
950
951                         while (!InteractiveBase.QuitRequested) {
952                                 try {
953                                         string error_string;
954                                         StringWriter error_output = (StringWriter)stderr;
955
956                                         string line = s.GetString ();
957         
958                                         bool result_set;
959                                         object result;
960         
961                                         if (input == null)
962                                                 input = line;
963                                         else
964                                                 input = input + "\n" + line;
965         
966                                         try {
967                                                 input = evaluator.Evaluate (input, out result, out result_set);
968                                         } catch (Exception e) {
969                                                 s.WriteByte ((byte) AgentStatus.ERROR);
970                                                 s.WriteString (e.ToString ());
971                                                 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
972                                                 continue;
973                                         }
974                                         
975                                         if (input != null){
976                                                 s.WriteByte ((byte) AgentStatus.PARTIAL_INPUT);
977                                                 continue;
978                                         }
979         
980                                         // Send warnings and errors back
981                                         error_string = error_output.ToString ();
982                                         if (error_string.Length != 0){
983                                                 s.WriteByte ((byte) AgentStatus.ERROR);
984                                                 s.WriteString (error_output.ToString ());
985                                                 error_output.GetStringBuilder ().Clear ();
986                                         }
987         
988                                         if (result_set){
989                                                 s.WriteByte ((byte) AgentStatus.RESULT_SET);
990                                                 StringWriter sr = new StringWriter ();
991                                                 CSharpShell.PrettyPrint (sr, result);
992                                                 s.WriteString (sr.ToString ());
993                                         } else {
994                                                 s.WriteByte ((byte) AgentStatus.RESULT_NOT_SET);
995                                         }
996                                 } catch (IOException) {
997                                         break;
998                                 } catch (Exception e){
999                                         Console.WriteLine (e);
1000                                 }
1001                         }
1002                 }
1003         }
1004
1005         public class UnixUtils {
1006                 [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
1007                 extern static int _isatty (int fd);
1008                         
1009                 public static bool isatty (int fd)
1010                 {
1011                         try {
1012                                 return _isatty (fd) == 1;
1013                         } catch {
1014                                 return false;
1015                         }
1016                 }
1017         }
1018 #endif
1019 }
1020         
1021 namespace Mono.Management
1022 {
1023         interface IVirtualMachine {
1024                 void LoadAgent (string filename, string args);
1025         }
1026 }
1027