* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / tools / cilc / cilc.cs
1 // cilc -- a CIL-to-C binding generator
2 // Copyright (C) 2003, 2004, 2005 Alp Toker <alp@atoker.com>
3 // Licensed under the terms of the GNU GPL
4
5 using System;
6 using System.IO;
7 using System.Reflection;
8 using System.Collections;
9 using System.Diagnostics;
10 using System.Text.RegularExpressions;
11
12 public class cilc
13 {
14         private cilc () {}
15
16         static CodeWriter C, H, Cindex, Hindex, Hdecls;
17         static string ns, dllname;
18         static string cur_type, CurType, CurTypeClass;
19         static string target_dir;
20
21         static ArrayList funcs_done = new ArrayList ();
22
23         public static int Main (string[] args)
24         {
25                 if (args.Length < 1 || args.Length > 4) {
26                         Console.WriteLine ("Mono CIL-to-C binding generator");
27                         Console.WriteLine ("Usage: cilc assembly [target] [pkg ns[,ns...]]");
28                         return 1;
29                 }
30
31                 ns = "Unnamed";
32
33                 RegisterByVal (typeof (uint));
34                 RegisterByVal (typeof (int));
35                 RegisterByVal (typeof (IntPtr));
36                 RegisterByVal (typeof (bool));
37                 RegisterByVal (typeof (char));
38                 RegisterByVal (typeof (sbyte));
39                 RegisterByVal (typeof (byte));
40                 RegisterByVal (typeof (double));
41
42                 if (args.Length == 1) {
43                         SmartBind (args[0]);
44                 } else if (args.Length == 2) {
45                         Generate (args[0], args[1]);
46                 } else if (args.Length == 3) {
47                         RegisterPkg (args[1], args[2]);
48                         SmartBind (args[0]);
49                 } else if (args.Length == 4) {
50                         RegisterPkg (args[2], args[3]);
51                         Generate (args[0], args[1]);
52                 }
53
54                 return 0;
55         }
56
57
58         public static void SmartBind (string aname)
59         {
60                 string tmpdir = Path.GetTempPath () + Path.GetTempFileName ();
61                 string cwd = Directory.GetCurrentDirectory ();
62                 if (Directory.Exists (tmpdir) || File.Exists (tmpdir)) {
63                         Console.WriteLine ("Error: Temporary directory " + tmpdir + " already exists.");
64                         return;
65                 }
66                 Generate (aname, tmpdir);
67                 Console.Write ("Compiling unmanaged binding");
68                 RunWithReport ("make", "-C \"" + tmpdir + "\" bundle=true");
69                 Console.WriteLine ();
70                 Console.Write ("Installing to current directory");
71                 RunWithReport ("make", "-C \"" + tmpdir + "\" install prefix=\"" + cwd + "\"");
72                 Directory.Delete (tmpdir, true);
73                 Console.WriteLine ();
74         }
75
76         public static string Run (string cmd, string args)
77         {
78                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
79                 psi.UseShellExecute = false;
80                 psi.RedirectStandardInput = true;
81                 psi.RedirectStandardOutput = true;
82                 psi.RedirectStandardError = true;
83
84                 Process p = Process.Start (psi);
85
86                 string line = p.StandardOutput.ReadLine ();
87
88                 p.WaitForExit ();
89
90                 return line;
91         }
92
93         public static int RunWithReport (string cmd, string args)
94         {
95                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
96                 psi.UseShellExecute = false;
97                 psi.RedirectStandardOutput = true;
98                 psi.RedirectStandardError = true;
99
100                 Process p = Process.Start (psi);
101
102                 string line;
103                 while ((line = p.StandardOutput.ReadLine ()) != null)
104                         if (verbose)
105                                 Console.WriteLine (line);
106                         else
107                                 Console.Write (".");
108
109                 Console.WriteLine ();
110
111                 Console.Write (p.StandardError.ReadToEnd ());
112
113                 p.WaitForExit ();
114
115                 return p.ExitCode;
116         }
117
118         static bool verbose = false;
119
120         static string extpkgs = "";
121         static string[] extsubpkgs = {};
122         static string[] extincludes = {};
123
124         public static void RegisterPkg (string pkg, string subpkgs)
125         {
126                 extpkgs += " " + pkg;
127
128                 string cflags = Run ("pkg-config", "--cflags-only-I " + pkg);
129
130                 extsubpkgs = subpkgs.Trim ().Split (',');
131                 extincludes = new string[extsubpkgs.Length];
132
133                 for (int i = 0 ; i != extsubpkgs.Length ; i++)
134                         extincludes[i] =  extsubpkgs[i] + "/" + extsubpkgs[i] + ".h";
135
136                 //string cmd = "gcc";
137                 //string args = "-E " + cflags + " " + includedir + "/" + hname;
138
139                 string cppincludes = "";
140                 foreach (string include in extincludes)
141                         cppincludes += " -include " + include;
142
143                 string cmd = "cpp";
144                 string args = cflags + cppincludes + " /dev/null";
145
146                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
147                 psi.UseShellExecute = false;
148                 psi.RedirectStandardOutput = true;
149                 psi.RedirectStandardError = true;
150
151                 Process p = Process.Start (psi);
152
153                 string line;
154
155                 Regex re_type = new Regex (@"typedef struct (_\w+) (\w+);");
156                 Regex re_enum = new Regex (@"} (\w+);");
157
158                 while ((line = p.StandardOutput.ReadLine ()) != null) {
159                         line = line.Trim ();
160
161                         Match m;
162
163                         m = re_type.Match (line);
164                         if (m.Success) {
165                                 string G = m.Groups[2].Value;
166
167                                 if (!GIsValid (G))
168                                         continue;
169
170                                 if (G.EndsWith ("Class"))
171                                         continue;
172
173                                 RegisterG (G);
174                                 continue;
175                         }
176
177                         m = re_enum.Match (line);
178                         if (m.Success) {
179                                 string G = m.Groups[1].Value;
180
181                                 if (!GIsValid (G))
182                                         continue;
183
184                                 RegisterG (G);
185                                 RegisterByVal (G);
186                                 continue;
187                         }
188                 }
189
190                 p.WaitForExit ();
191                 Console.Write (p.StandardError.ReadToEnd ());
192                 Console.WriteLine ();
193         }
194
195         static bool GIsValid (string G) {
196                 foreach (string extsubpkg in extsubpkgs)
197                         if (G.ToLower ().StartsWith (extsubpkg))
198                                 return true;
199
200                 return false;
201         }
202
203         public static void Generate (string assembly, string target)
204         {
205                 target_dir = target + Path.DirectorySeparatorChar;
206
207                 if (Directory.Exists (target_dir)) {
208                         Console.WriteLine ("Error: Target directory " + target_dir + " already exists.");
209                         return;
210                 }
211
212                 Directory.CreateDirectory (target_dir);
213
214                 Assembly a = Assembly.LoadFrom (assembly);
215
216                 Console.WriteLine ();
217                 Console.WriteLine ("References (not followed):");
218                 foreach (AssemblyName reference in a.GetReferencedAssemblies ())
219                         Console.WriteLine ("  " + reference.Name);
220                 Console.WriteLine ();
221
222                 dllname = Path.GetFileName (assembly);
223                 AssemblyGen (a);
224
225                 //we might not want to do this in future
226                 File.Copy (dllname, target_dir + dllname);
227
228                 string soname = "lib" + NsToFlat (Path.GetFileNameWithoutExtension (assembly)).ToLower () + ".so";
229
230                 //create the static makefile
231                 StreamWriter makefile = new StreamWriter (File.Create (target_dir + "Makefile"));
232                 StreamReader sr = new StreamReader (Assembly.GetAssembly (typeof(cilc)).GetManifestResourceStream ("res-Makefile"));
233
234                 makefile.Write (sr.ReadToEnd ());
235                 sr.Close ();
236                 makefile.Close ();
237
238                 //create makefile defs
239                 CodeWriter makefile_defs = new CodeWriter (target_dir + "defs.mk");
240                 makefile_defs.Indenter = "\t";
241                 makefile_defs.WriteLine ("ASSEMBLY = " + assembly);
242                 makefile_defs.WriteLine ("SONAME = " + soname);
243                 makefile_defs.WriteLine (@"OBJS = $(shell ls *.c | sed -e 's/\.c/.o/')");
244
245                 if (extpkgs != "") {
246                         makefile_defs.WriteLine ("EXTRAINCLUDES = $(shell pkg-config --cflags" + extpkgs + ")");
247                         makefile_defs.WriteLine ("EXTRALIBS = $(shell pkg-config --libs" + extpkgs + ")");
248                 }
249                 makefile_defs.Close ();
250
251                 Console.WriteLine ();
252
253                 //identify hits on types that were registered too late
254                 foreach (string tn in registered_types) {
255                         if (registry_hits.Contains (tn)) {
256                                 Console.WriteLine ("Warning: " + tn + " was incorrectly registered after it was needed instead of before. Consider re-ordering.");
257                         }
258                 }
259
260                 MakeReport (registry_hits, "Type registry missed hits", 20);
261                 Console.WriteLine ();
262                 //TODO: this count is now wrong
263                 Console.WriteLine (registered_types.Count + " types generated/seen in " + namespaces.Length + " namespaces; " + warnings_ignored + " types ignored");
264                 Console.WriteLine ();
265         }
266
267         static void MakeReport (Hashtable ctable, string desc, int num)
268         {
269                 Console.WriteLine (desc + " (top " + (registry_hits.Count > num ? num : registry_hits.Count) + " of " + registry_hits.Count + "):");
270                 string[] reg_keys = (string[]) (new ArrayList (ctable.Keys)).ToArray (typeof (string));
271                 int[] reg_vals = (int[]) (new ArrayList (ctable.Values)).ToArray (typeof (int));
272                 Array.Sort (reg_vals, reg_keys);
273
274                 Array.Reverse (reg_vals);
275                 Array.Reverse (reg_keys);
276
277                 for (int i = 0 ; i != reg_keys.Length && i != num ; i++) {
278                         Console.WriteLine ("  " + reg_keys[i] + ": " + reg_vals[i]);
279                 }
280         }
281
282         static int warnings_ignored = 0;
283
284         static void AssemblyGen (Assembly a)
285         {
286                 Type[] types = a.GetTypes ();
287                 Hashtable ns_types = new Hashtable ();
288
289                 foreach (Type t in types) {
290                         if (t.IsNotPublic) {
291                                 //Console.WriteLine ("Ignoring non-public type: " + t.Name);
292                                 //warnings_ignored++;
293                                 continue;
294                         }
295
296                         if (!t.IsClass && !t.IsEnum) {
297                                 //Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
298                                 warnings_ignored++;
299                                 continue;
300                         }
301
302                         RegisterType (t);
303
304                         if (t.IsEnum)
305                                 RegisterByVal (t);
306
307                         string tns = t.Namespace == null ? "" : t.Namespace;
308
309                         if (!ns_types.Contains (tns))
310                                 ns_types[tns] = new ArrayList ();
311
312                         ((ArrayList) ns_types[tns]).Add (t);
313                 }
314
315                 namespaces = (string[]) (new ArrayList (ns_types.Keys)).ToArray (typeof (string));
316
317                 foreach (DictionaryEntry de in ns_types)
318                         NamespaceGen ((string) de.Key, (Type[]) ((ArrayList) de.Value).ToArray (typeof (Type)));
319         }
320
321         static string[] namespaces;
322
323         static void NamespaceGen (string given_ns, Type[] types)
324         {
325                 //ns = types[0].Namespace;
326                 ns = given_ns;
327
328                 Hindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".h");
329                 Hdecls = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + "types.h");
330                 Cindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".c");
331
332                 string Hindex_id = "__" + NsToFlat (ns).ToUpper () + "_H__";
333                 Hindex.WriteLine ("#ifndef " + Hindex_id);
334                 Hindex.WriteLine ("#define " + Hindex_id);
335                 Hindex.WriteLine ();
336
337                 string Hdecls_id = "__" + NsToFlat (ns).ToUpper () + "_DECLS_H__";
338                 Hdecls.WriteLine ("#ifndef " + Hdecls_id);
339                 Hdecls.WriteLine ("#define " + Hdecls_id);
340                 Hdecls.WriteLine ();
341
342                 Cindex.WriteLine ("#include <glib.h>");
343                 Cindex.WriteLine ("#include <glib-object.h>");
344                 Cindex.WriteLine ("#include <mono/jit/jit.h>");
345                 Cindex.WriteLine ();
346                 Cindex.WriteLine ("#include <mono/metadata/object.h>");
347                 Cindex.WriteLine ("#include <mono/metadata/debug-helpers.h>");
348                 Cindex.WriteLine ("#include <mono/metadata/appdomain.h>");
349                 Cindex.WriteLine ();
350                 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
351                 Cindex.WriteLine ("#include \"bundle.h\"");
352                 Cindex.WriteLine ("#endif");
353                 Cindex.WriteLine ();
354
355                 Cindex.WriteLine ("MonoDomain *" + NsToC (ns) + "_get_mono_domain (void)");
356                 Cindex.WriteLine ("{");
357                 Cindex.WriteLine ("static MonoDomain *domain = NULL;");
358                 Cindex.WriteLine ("if (domain != NULL) return domain;");
359                 Cindex.WriteLine ("mono_config_parse (NULL);");
360                 Cindex.WriteLine ("domain = mono_jit_init (\"cilc\");");
361                 Cindex.WriteLine ();
362                 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
363                 Cindex.WriteLine ("mono_register_bundled_assemblies (bundled);");
364                 Cindex.WriteLine ("#endif");
365                 Cindex.WriteLine ();
366
367                 Cindex.WriteLine ("return domain;");
368                 Cindex.WriteLine ("}");
369                 Cindex.WriteLine ();
370
371                 Cindex.WriteLine ("MonoAssembly *" + NsToC (ns) + "_get_mono_assembly (void)");
372                 Cindex.WriteLine ("{");
373                 Cindex.WriteLine ("static MonoAssembly *assembly = NULL;");
374                 Cindex.WriteLine ("if (assembly != NULL) return assembly;");
375                 Cindex.WriteLine ("assembly = mono_domain_assembly_open (" + NsToC (ns) + "_get_mono_domain (), \"" + dllname + "\");");
376                 Cindex.WriteLine ();
377
378                 Cindex.WriteLine ("return assembly;");
379                 Cindex.WriteLine ("}");
380                 Cindex.WriteLine ();
381
382
383                 Cindex.WriteLine ("MonoObject *" + NsToC (ns) + "_cilc_glib_gobject_get_mobject (GObject *_handle)");
384                 Cindex.WriteLine ("{");
385                 //FIXME: instantiate monobject if it doesn't exist
386                 Cindex.WriteLine ("return g_object_get_data (G_OBJECT (" + "_handle" + "), \"mono-object\");");
387                 Cindex.WriteLine ("}");
388
389                 Cindex.WriteLine ("gpointer " + NsToC (ns) + "_cilc_glib_mobject_get_gobject (MonoObject *_mono_object)");
390                 Cindex.WriteLine ("{");
391                 Cindex.WriteLine ("static MonoAssembly *_mono_assembly = NULL;");
392                 Cindex.WriteLine ("static MonoMethod *_mono_method = NULL;");
393                 Cindex.WriteLine ("static MonoClass *_mono_class = NULL;");
394                 Cindex.WriteLine ("gpointer *retval;");
395                 Cindex.WriteLine ();
396                 Cindex.WriteLine ("if (_mono_assembly == NULL) {");
397                 Cindex.WriteLine ("_mono_assembly = mono_domain_assembly_open (" + NsToC (ns) + "_get_mono_domain (), \"" + "glib-sharp" + "\");");
398                 Cindex.WriteLine ("}");
399                 Cindex.WriteLine ("if (_mono_class == NULL) {");
400                 Cindex.WriteLine ("_mono_class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (_mono_assembly), \"GLib\", \"Object\");");
401                 Cindex.WriteLine ("}");
402                 Cindex.WriteLine ("if (_mono_method == NULL) {");
403                 Cindex.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":get_Handle()\", FALSE);");
404                 Cindex.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, _mono_class);");
405                 Cindex.WriteLine ("}");
406                 Cindex.WriteLine ();
407                 Cindex.WriteLine ("retval = (gpointer *) mono_object_unbox (mono_runtime_invoke (_mono_method, _mono_object, NULL, NULL));");
408                 Cindex.WriteLine ("return (gpointer ) *retval;");
409                 Cindex.WriteLine ("}");
410                 Cindex.WriteLine ();
411
412
413                 Console.Write ("Generating sources in " + ns);
414                 foreach (Type t in types) {
415                         TypeGen (t);
416                         Console.Write (".");
417                 }
418
419                 Console.WriteLine ();
420
421                 Hindex.WriteLine ();
422                 Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
423
424                 Hdecls.WriteLine ();
425                 Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
426
427                 Cindex.Close ();
428                 Hindex.Close ();
429                 Hdecls.Close ();
430         }
431
432         static void TypeGen (Type t)
433         {
434                 //TODO: we only handle ordinary classes for now
435                 /*
436                          else if (t.IsSubclassOf (typeof (Delegate))) {
437                          Console.WriteLine ("Ignoring delegate: " + t.Name);
438                          return;
439                          }
440                          */
441
442                 cur_type = NsToC (ns) + "_" + CamelToC (t.Name);
443                 //CurType = NsToFlat (ns) + t.Name;
444                 CurType = CsTypeToG (t);
445                 CurTypeClass = GToGC (CurType);
446
447                 //ns = t.Namespace;
448                 string fname = NsToFlat (ns).ToLower () + t.Name.ToLower ();
449                 C = new CodeWriter (target_dir + fname + ".c");
450                 H = new CodeWriter (target_dir + fname + ".h");
451                 Hindex.WriteLine ("#include <" + fname + ".h" + ">");
452
453
454                 string H_id = "__" + NsToFlat (ns).ToUpper () + "_" + t.Name.ToUpper () + "_H__";
455                 H.WriteLine ("#ifndef " + H_id);
456                 H.WriteLine ("#define " + H_id);
457                 H.WriteLine ();
458
459                 H.WriteLine ("#include <glib.h>");
460                 H.WriteLine ("#include <glib-object.h>");
461
462                 foreach (string include in extincludes)
463                         H.WriteLine ("#include <" + include + ">");
464
465                 H.WriteLine ();
466
467                 if (IsRegistered (t.BaseType) && !IsExternal (t.BaseType))
468                         H.WriteLine ("#include \"" + NsToFlat (t.BaseType.Namespace).ToLower () + t.BaseType.Name.ToLower () + ".h\"");
469
470                 foreach (string ext_ns in namespaces)
471                         H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "types.h\"");
472
473                 H.WriteLine ();
474
475                 H.WriteLine ("#ifdef __cplusplus");
476                 H.WriteLine ("extern \"C\" {", false);
477                 H.WriteLine ("#endif /* __cplusplus */");
478                 H.WriteLine ();
479
480                 C.WriteLine ("#include \"" + fname + ".h" + "\"");
481                 C.WriteLine ("#include <mono/metadata/object.h>");
482                 C.WriteLine ("#include <mono/metadata/debug-helpers.h>");
483                 C.WriteLine ("#include <mono/metadata/appdomain.h>");
484                 C.WriteLine ();
485
486                 if (t.IsClass)
487                         ClassGen (t);
488                 else if (t.IsEnum)
489                         EnumGen (t);
490
491                 H.WriteLine ();
492                 H.WriteLine ("#ifdef __cplusplus");
493                 H.WriteLine ("}", false);
494                 H.WriteLine ("#endif /* __cplusplus */");
495                 H.WriteLine ();
496
497                 H.WriteLine ("#endif /* " + H_id + " */");
498
499                 C.Close ();
500                 H.Close ();
501         }
502
503         static void EnumGen (Type t)
504         {
505                 //TODO: we needn't split out each enum into its own file
506
507                 string gname = CsTypeToG (t);
508
509                 Hdecls.WriteLine ("typedef enum");
510                 Hdecls.WriteLine ("{");
511                 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
512                 C.WriteLine ("{");
513                 C.WriteLine ("static GType etype = 0;");
514                 C.WriteLine ("if (etype == 0) {");
515                 C.WriteLine ("static const GEnumValue values[] = {");
516                 foreach (FieldInfo fi in t.GetFields (BindingFlags.Static|BindingFlags.Public)) {
517                         string finame = (cur_type + "_" + CamelToC (fi.Name)).ToUpper ();
518                         Hdecls.WriteLine (finame + ",");
519                         C.WriteLine ("{ " + finame + ", \"" + finame + "\", \"" + CamelToC (fi.Name).Replace ("_", "-") + "\" },");
520                 }
521                 Hdecls.WriteLine ("} " + gname + ";");
522                 Hdecls.WriteLine ();
523                 C.WriteLine ("{ 0, NULL, NULL }");
524                 C.WriteLine ("};");
525                 C.WriteLine ("etype = g_enum_register_static (\"" + gname + "\", values);");
526                 C.WriteLine ("}");
527                 C.WriteLine ("return etype;");
528                 C.WriteLine ("}");
529         }
530
531         static void ClassGen (Type t)
532         {
533                 //TODO: what flags do we want for GetEvents and GetConstructors?
534
535                 //events as signals
536                 EventInfo[] events;
537                 events = t.GetEvents (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
538
539                 Type[] ifaces;
540                 ifaces = t.GetInterfaces ();
541
542                 H.WriteLine ("G_BEGIN_DECLS");
543                 H.WriteLine ();
544
545                 {
546                         string NS = NsToC (ns).ToUpper ();
547                         string T = CamelToC (t.Name).ToUpper ();
548                         string NST = NS + "_" + T;
549                         string NSTT = NS + "_TYPE_" + T;
550
551                         H.WriteLine ("#define " + NSTT + " (" + cur_type + "_get_type ())");
552                         H.WriteLine ("#define " + NST + "(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), " + NSTT + ", " + CurType + "))");
553                         H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurTypeClass + "))");
554                         H.WriteLine ("#define " + NS + "_IS_" + T + "(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), " + NSTT + "))");
555                         H.WriteLine ("#define " + NS + "_IS_" + T + "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT + "))");
556                         H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurTypeClass + "))");
557                 }
558
559                 if (!C.IsDuplicate) {
560                         Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
561                         Hdecls.WriteLine ("typedef struct _" + CurTypeClass + " " + CurTypeClass + ";");
562                         Hdecls.WriteLine ();
563                 }
564
565                 H.WriteLine ();
566                 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
567
568                 //H.WriteLine ();
569                 //H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
570                 H.WriteLine ("typedef struct _" + CurType + "Private " + CurType + "Private;");
571                 H.WriteLine ();
572                 H.WriteLine ("struct _" + CurType);
573                 H.WriteLine ("{");
574
575                 string ParentName = CsTypeToG (t.BaseType);
576                 string ParentNameClass = GToGC (ParentName);
577
578                 H.WriteLine (ParentName + " parent_instance;");
579                 H.WriteLine (CurType + "Private *priv;");
580                 H.WriteLine ("};");
581                 H.WriteLine ();
582                 H.WriteLine ("struct _" + CurTypeClass);
583                 H.WriteLine ("{");
584                 H.WriteLine (ParentNameClass + " parent_class;" + " /* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
585
586                 if (events.Length != 0)
587                         H.WriteLine ();
588
589                 //TODO: event arguments
590                 foreach (EventInfo ei in events)
591                         H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
592
593                 H.WriteLine ("};");
594                 H.WriteLine ();
595
596                 //generate c file
597
598                 //private struct
599                 C.WriteLine ("struct _" + CurType + "Private");
600                 C.WriteLine ("{");
601                 C.WriteLine ("MonoObject *mono_object;");
602                 C.WriteLine ("};");
603
604                 C.WriteLine ();
605
606                 //events
607                 if (events.Length != 0) {
608                         C.WriteLine ("enum {");
609
610                         foreach (EventInfo ei in events)
611                                 C.WriteLine (CamelToC (ei.Name).ToUpper () + ",");
612
613                         C.WriteLine ("LAST_SIGNAL");
614                         C.WriteLine ("};");
615                         C.WriteLine ();
616                 }
617
618                 C.WriteLine ("static gpointer parent_class;");
619
620                 if (events.Length == 0)
621                         C.WriteLine ("static guint signals[0];");
622                 else
623                         C.WriteLine ("static guint signals[LAST_SIGNAL] = { 0 };");
624                 C.WriteLine ();
625
626                 C.WriteLine ("static MonoClass *" + cur_type + "_get_mono_class (void)");
627                 C.WriteLine ("{");
628                 C.WriteLine ("MonoAssembly *assembly;");
629                 C.WriteLine ("static MonoClass *class = NULL;");
630                 C.WriteLine ("if (class != NULL) return class;");
631
632                 C.WriteLine ("assembly = (MonoAssembly*) " + NsToC (ns) + "_get_mono_assembly ();");
633                 C.WriteLine ("class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (assembly)" + ", \"" + ns + "\", \"" + t.Name + "\");");
634
635                 C.WriteLine ("mono_class_init (class);");
636                 C.WriteLine ();
637
638                 C.WriteLine ("return class;");
639                 C.WriteLine ("}");
640
641                 C.WriteLine ();
642
643                 //generate the GObject init function
644                 C.WriteLine ("static void " + cur_type + "_init (" + CurType + " *thiz" + ")");
645                 C.WriteLine ("{");
646                 C.WriteLine ("thiz->priv = g_new0 (" + CurType + "Private, 1);");
647                 C.WriteLine ("}");
648
649                 C.WriteLine ();
650
651
652                 //generate the GObject class init function
653                 C.WriteLine ("static void " + cur_type + "_class_init (" + CurTypeClass + " *klass" + ")");
654                 C.WriteLine ("{");
655
656                 C.WriteLine ("GObjectClass *object_class = G_OBJECT_CLASS (klass);");
657                 C.WriteLine ("parent_class = g_type_class_peek_parent (klass);");
658                 //C.WriteLine ("object_class->finalize = _finalize;");
659
660                 foreach (EventInfo ei in events)
661                         EventGen (ei, t);
662
663                 C.WriteLine ("}");
664
665                 C.WriteLine ();
666                 C.WriteLine ("static void " + cur_type + "_interface_init (gpointer g_iface, gpointer iface_data)");
667                 C.WriteLine ("{");
668                 
669                 foreach (Type iface in ifaces) {
670                         C.WriteLine ("//" + CsTypeToG (iface) + "Interface" + " *iface = (" + CsTypeToG (iface) + "Interface" + ")g_iface;");
671                         foreach (MethodInfo imi in iface.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly)) {
672                                 string funcname = ToValidFuncName (CamelToC (imi.Name));
673                                 C.WriteLine ("//iface->" + funcname + " = " + cur_type + "_" + funcname + ";");
674                         }
675                         //TODO: properties etc.
676                 }
677                 
678                 C.WriteLine ("}");
679
680                 C.WriteLine ();
681
682                 //generate the GObject get_type function
683                 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
684                 C.WriteLine ("{");
685                 C.WriteLine ("static GType object_type = 0;");
686                 C.WriteLine ("g_type_init ();");
687                 C.WriteLine ();
688                 C.WriteLine ("if (object_type) return object_type;");
689                 C.WriteLine ();
690                 C.WriteLine ("static const GTypeInfo object_info =");
691                 C.WriteLine ("{");
692                 C.WriteLine ("sizeof (" + CurTypeClass + "),");
693                 C.WriteLine ("(GBaseInitFunc) NULL, /* base_init */");
694                 C.WriteLine ("(GBaseFinalizeFunc) NULL, /* base_finalize */");
695                 C.WriteLine ("(GClassInitFunc) " + cur_type + "_class_init, /* class_init */");
696                 C.WriteLine ("NULL, /* class_finalize */");
697                 C.WriteLine ("NULL, /* class_data */");
698                 C.WriteLine ("sizeof (" + CurType + "),");
699                 C.WriteLine ("0, /* n_preallocs */");
700                 C.WriteLine ("(GInstanceInitFunc) " + cur_type + "_init,");
701                 C.WriteLine ("};");
702                 C.WriteLine ();
703
704                 foreach (Type iface in ifaces) {
705                         C.WriteLine ("//static const GInterfaceInfo bar_ifoo_info = {};");
706                 }
707
708                 string parent_type = "G_TYPE_OBJECT";
709                 if (IsRegistered (t.BaseType))
710                         parent_type = NsToC (t.BaseType.Namespace).ToUpper () + "_TYPE_" + CamelToC (t.BaseType.Name).ToUpper ();
711
712                 C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
713
714                 foreach (Type iface in ifaces)
715                         C.WriteLine ("//g_type_add_interface_static (object_type, BAR_IFOO_TYPE, bar_ifoo_info);");
716                 C.WriteLine ();
717                 C.WriteLine ("return object_type;");
718                 C.WriteLine ("}");
719
720                 wrap_gobject = TypeIsGObject (t);
721
722                 //generate constructors
723                 ConstructorInfo[] constructors;
724                 constructors = t.GetConstructors ();
725                 foreach (ConstructorInfo c in constructors)
726                         ConstructorGen (c, t);
727
728                 //generate static methods
729                 MethodInfo[] methods;
730                 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Static|BindingFlags.DeclaredOnly);
731                 foreach (MethodInfo m in methods)
732                         MethodGen (m, t);
733
734                 //generate instance methods
735                 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
736                 foreach (MethodInfo m in methods)
737                         MethodGen (m, t);
738
739                 H.WriteLine ();
740                 H.WriteLine ("G_END_DECLS");
741         }
742
743         static bool TypeIsGObject (Type t)
744         {
745                 if (t == null)
746                         return false;
747
748                 if (t.FullName == "GLib.Object")
749                         return true;
750
751                 return TypeIsGObject (t.BaseType);
752         }
753
754
755         //FIXME: clean up this mess with hits as the type registry uses strings
756
757         static ArrayList registered_types = new ArrayList ();
758         static ArrayList byval_types = new ArrayList ();
759         static ArrayList external_types = new ArrayList ();
760         static Hashtable registry_hits = new Hashtable ();
761
762         static bool IsRegisteredByVal (Type t)
763         {
764                 return byval_types.Contains (CsTypeToFlat (t));
765         }
766
767         static bool IsExternal (Type t)
768         {
769                 return external_types.Contains (CsTypeToFlat (t));
770         }
771
772         static void RegisterByVal (string tn)
773         {
774                 //TODO: warn on dupes
775                 byval_types.Add (tn);
776         }
777
778         static void RegisterByVal (Type t)
779         {
780                 RegisterByVal (CsTypeToFlat (t));
781         }
782
783         static bool IsRegistered (String tn)
784         {
785                 return registered_types.Contains (tn);
786         }
787
788         static bool IsRegistered (Type t)
789         {
790                 return IsRegistered (t, true);
791         }
792
793         static bool IsRegistered (Type t, bool log_hits)
794         {
795                 return IsRegistered (CsTypeToFlat (t), true);
796         }
797
798         static bool IsRegistered (string tn, bool log_hits)
799         {
800                 //bool isreg = registered_types.Contains (t);
801                 bool isreg = registered_types.Contains (tn);
802
803                 if (!isreg && log_hits) {
804                         HitRegistry (tn);
805                 }
806
807                 return isreg;
808         }
809
810         static void HitRegistry (string tn)
811         {
812                 //FIXME: ignore handled primitive types here
813
814                 if (!registry_hits.Contains (tn)) {
815                         int count = 0;
816                         registry_hits[tn] = count;
817                 }
818
819                 registry_hits[tn] = (int) registry_hits[tn] + 1;
820         }
821
822         static bool RegisterG (string G)
823         {
824                 if (IsRegistered (G, false)) {
825                         Console.WriteLine ("Warning: unmanaged type " + G + " already registered! Can't re-register.");
826                         return false;
827                 }
828
829                 external_types.Add (G);
830                 registered_types.Add (G);
831                 return true;
832         }
833
834         static string NewG (string G)
835         {
836                 if (IsRegistered (G, false))
837                 {
838                         Console.WriteLine ("Warning: type " + G + " already registered! Appending 'Extra' and trying again");
839                         Console.WriteLine ();
840                         return NewG (G + "Extra"); //FIXME: handle this properly
841                 }
842
843                 registered_types.Add (G);
844                 return (G);
845         }
846
847         static string CsTypeToFlat (Type t) //TODO: use this everywhere
848         {
849                 //TODO: check registry to see if t.Name's name has been changed during NewG.
850                 //if it's not in the registry, continue as usual
851
852                 return NsToFlat (t.Namespace) + t.Name;
853         }
854
855         static void RegisterType (Type t)
856         {
857                 NewG (CsTypeToFlat (t));
858         }
859
860         /*
861         static string NewG (Type t)
862         {
863                 return NewG (CsTypeToFlat (t));
864         }
865         */
866
867         static string CsTypeToG (Type t)
868         {
869                 if (IsRegistered (t))
870                         return CsTypeToFlat (t);
871
872                 return "GObject";
873         }
874
875         //static string CsTypeToGC (String tn)
876         static string GToGC (string G)
877         {
878                 string possGC = G + "Class";
879
880                 if (IsRegistered (possGC))
881                         return GToGC (G + "Object");
882                 else
883                         return possGC;
884         }
885
886         static string CsTypeToC (Type t)
887         {
888                 //TODO: use this method everywhere
889
890                 switch (t.FullName)
891                 {
892                         case "System.String":
893                                 return "const gchar *";
894
895                         case "System.Int32":
896                                 return "gint ";
897
898                         case "System.UInt32":
899                                 return "guint ";
900
901                         case "System.Boolean":
902                                 return "gboolean ";
903
904                         case "System.IntPtr":
905                                 return "gpointer ";
906                         
907                         case "System.Char":
908                                 return "guint16 ";
909
910                         case "System.SByte":
911                                 return "gint8 ";
912
913                         case "System.Byte":
914                                 return "guint8 ";
915
916                         case "System.Double":
917                                 return "gdouble ";
918
919                         //questionable
920                         case "System.EventHandler":
921                                 case "System.MulticastDelegate":
922                                 return "GCallback ";
923                 }
924
925                 if (IsRegistered (t) && IsRegisteredByVal (t))
926                         return CsTypeToFlat (t) + " ";
927
928                 if (t == typeof (void))
929                         return "void ";
930
931                 return CsTypeToG (t) + " *";
932         }
933
934         static void EventGen (EventInfo ei, Type t)
935         {
936                 //Console.WriteLine ("TODO: event: " + ei.Name);
937                 //Console.WriteLine ("\t" + CamelToC (ei.Name));
938                 string name = CamelToC (ei.Name);
939
940                 C.WriteLine ();
941                 C.WriteLine ("signals[" + name.ToUpper () + "] = g_signal_new (");
942                 C.WriteLine ("\"" + name + "\",");
943                 C.WriteLine ("G_OBJECT_CLASS_TYPE (object_class),");
944                 C.WriteLine ("G_SIGNAL_RUN_LAST,");
945                 C.WriteLine ("G_STRUCT_OFFSET (" + CurTypeClass + ", " + name + "),");
946                 C.WriteLine ("NULL, NULL,");
947                 C.WriteLine ("g_cclosure_marshal_VOID__VOID,");
948                 C.WriteLine ("G_TYPE_NONE, 0");
949                 C.WriteLine (");");
950         }
951
952         static void ConstructorGen (ConstructorInfo c, Type t)
953         {
954                 ParameterInfo[] parameters = c.GetParameters ();
955                 FunctionGen (parameters, (MethodBase) c, t, null, true);
956         }
957
958         static void MethodGen (MethodInfo m, Type t)
959         {
960                 ParameterInfo[] parameters = m.GetParameters ();
961                 FunctionGen (parameters, (MethodBase) m, t, m.ReturnType, false);
962         }
963
964         static readonly string[] keywords = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"};
965
966         static string KeywordAvoid (string s)
967         {
968                 if (Array.IndexOf (keywords, s.ToLower ()) != -1)
969                         return KeywordAvoid ("_" + s);
970
971                 return s;
972         }
973
974         static string ToValidFuncName (string name)
975         {
976                 //avoid generated function name conflicts with internal functions
977
978                 switch (name.ToLower ()) {
979                         case "init":
980                                 return "initialize";
981                         case "class_init":
982                                 return "class_initialize";
983                         case "get_type":
984                                 return "retrieve_type";
985                         default:
986                         return name;
987                 }
988         }
989
990         static bool wrap_gobject = false;
991
992         static void FunctionGen (ParameterInfo[] parameters, MethodBase m, Type t, Type ret_type, bool ctor)
993         {
994                 string myargs = "";
995                 bool has_return = !ctor && ret_type != null && ret_type != typeof (void);
996                 bool stat = m.IsStatic;
997
998                 string mytype, rettype;
999
1000                 mytype = CurType + " *";
1001
1002                 if (ctor) {
1003                         has_return = true;
1004                         rettype = mytype;
1005                         stat = true;
1006                 } else
1007                         rettype = CsTypeToC (ret_type);
1008
1009                 string params_arg = "NULL";
1010                 if (parameters.Length != 0)
1011                         params_arg = "_mono_params";
1012
1013                 string instance = "thiz";
1014                 string mono_obj = "NULL";
1015
1016                 if (ctor || !stat)
1017                         mono_obj = "_mono_object";
1018
1019                 //if (ctor || !stat)
1020                 //      mono_obj = instance + "->priv->mono_object";
1021
1022                 if (!stat) {
1023                         myargs = mytype + instance;
1024                         if (parameters.Length > 0) myargs += ", ";
1025                 }
1026
1027                 string myname;
1028
1029                 myname = cur_type + "_";
1030                 if (ctor)
1031                         myname += "new";
1032                 else
1033                         myname += ToValidFuncName (CamelToC (m.Name));
1034
1035                 //handle overloaded methods
1036                 //TODO: generate an alias function for the default ctor etc.
1037
1038                 //TODO: how do we choose the default ctor/method overload? perhaps the
1039                 //first/shortest, but we need scope for this
1040                 //perhaps use DefaultMemberAttribute, Type.GetDefaultMembers
1041
1042                 if (funcs_done.Contains (myname)) {
1043                         for (int i = 0 ; i < parameters.Length ; i++) {
1044                                 ParameterInfo p = parameters[i];
1045
1046                                 if (i == 0)
1047                                         myname += "_with_";
1048                                 else
1049                                         myname += "_and_";
1050
1051                                 myname += KeywordAvoid (p.Name);
1052                         }
1053                 }
1054
1055                 if (funcs_done.Contains (myname))
1056                         return;
1057
1058                 funcs_done.Add (myname);
1059
1060                 //handle the parameters
1061                 string mycsargs = "";
1062
1063                 for (int i = 0 ; i < parameters.Length ; i++) {
1064                         ParameterInfo p = parameters[i];
1065                         mycsargs += GetMonoType (Type.GetTypeCode (p.ParameterType));
1066                         myargs += CsTypeToC (p.ParameterType) + KeywordAvoid (p.Name);
1067                         if (i != parameters.Length - 1) {
1068                                 mycsargs += ",";
1069                                 myargs += ", ";
1070                         }
1071                 }
1072
1073                 if (myargs == "")
1074                         myargs = "void";
1075
1076                 C.WriteLine ();
1077
1078                 C.WriteLine (rettype + myname + " (" + myargs + ")", H, ";");
1079
1080                 C.WriteLine ("{");
1081
1082                 C.WriteLine ("static MonoMethod *_mono_method = NULL;");
1083
1084                 if (ctor || !stat)
1085                         C.WriteLine ("MonoObject *" + mono_obj + ";");
1086
1087                 if (parameters.Length != 0) C.WriteLine ("gpointer " + params_arg + "[" + parameters.Length + "];");
1088
1089                 if (ctor) {
1090                         C.WriteLine (CurType + " *" + instance + ";");
1091                 }
1092
1093                 if (!ctor && !stat) {
1094                         C.WriteLine ();
1095                         C.WriteLine (mono_obj + " = g_object_get_data (G_OBJECT (" + instance + "), \"mono-object\");");
1096                 }
1097
1098                 C.WriteLine ();
1099
1100                 C.WriteLine ("if (_mono_method == NULL) {");
1101
1102                 if (ctor)
1103                         C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
1104                 else
1105                         C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":" + m.Name + "(" + mycsargs + ")" + "\", FALSE);");
1106
1107
1108                 C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
1109
1110                 C.WriteLine ("}");
1111                 C.WriteLine ();
1112
1113                 //assign the parameters
1114                 for (int i = 0 ; i < parameters.Length ; i++) {
1115                         ParameterInfo p = parameters[i];
1116                         C.WriteLine (params_arg + "[" + i + "] = " + GetMonoVal (p.ParameterType, KeywordAvoid (p.Name)) + ";");
1117                 }
1118
1119                 if (parameters.Length != 0)
1120                         C.WriteLine ();
1121
1122                 if (ctor)
1123                         C.WriteLine (mono_obj + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
1124
1125                 //delegates are a special case as we want their constructor to take a function pointer
1126                 if (ctor && t.IsSubclassOf (typeof (MulticastDelegate))) {
1127                         C.WriteLine ("mono_delegate_ctor (" + mono_obj + ", object, method);");
1128                 } else {
1129                         //code to invoke the method
1130
1131                         if (!ctor && has_return)
1132                                 if (IsRegisteredByVal (ret_type)) {
1133                                         C.WriteLine ("{");
1134                                         C.WriteLine (rettype + "* retval = (" + rettype + "*) mono_object_unbox (mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1135                                         C.WriteLine ("return (" + rettype + ") *retval;");
1136                                         C.WriteLine ("}");
1137                                 } else if (rettype == "const gchar *")
1138                                 {
1139                                         //convert the MonoString to a UTF8 before returning
1140                                         C.WriteLine ("return (" + rettype + ") mono_string_to_utf8 ((MonoString*) mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1141                                 } else {
1142                                         //TODO: this isn't right
1143                                         C.WriteLine ("return (" + rettype + ") mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1144                                 }
1145                                 else
1146                                         C.WriteLine ("mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1147                 }
1148
1149                 if (ctor) {
1150                         C.WriteLine ();
1151
1152                         //TODO: use ->priv, not data for better performance if not wrapping a gobject
1153                         if (wrap_gobject)
1154                                 C.WriteLine (instance + " = (" + CurType + " *) " + NsToC (ns) + "_cilc_glib_mobject_get_gobject (" + mono_obj + ");");
1155                         else
1156                                 C.WriteLine (instance + " = (" + CurType + " *) g_object_new (" + NsToC (ns).ToUpper () + "_TYPE_" + CamelToC (t.Name).ToUpper () + ", NULL);");
1157
1158                         C.WriteLine ("g_object_set_data (G_OBJECT (" + instance + "), \"mono-object\", " + mono_obj + ");");
1159                         C.WriteLine ();
1160                         C.WriteLine ("return " + instance + ";");
1161                 }
1162
1163                 C.WriteLine ("}");
1164         }
1165
1166         static string GetMonoType (TypeCode tc)
1167         {
1168                 //see mcs/class/corlib/System/TypeCode.cs
1169                 //see mono/mono/dis/get.c
1170
1171                 switch (tc)
1172                 {
1173                         case TypeCode.Int32:
1174                                 return "int";
1175
1176                         case TypeCode.String:
1177                                 return "string";
1178
1179                         default: //TODO: construct signature based on mono docs
1180                                 return tc.ToString ().ToLower ();
1181                 }
1182         }
1183
1184         static string GetMonoVal (Type t, string name)
1185         {
1186                 string type = t.FullName;
1187
1188                 if (TypeIsGObject (t))
1189                         return "(gpointer*) " + NsToC (ns) + "_cilc_glib_gobject_get_mobject (G_OBJECT (" + name + "))";
1190
1191                 switch (type) {
1192                         case "System.String":
1193                                 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name + ")";
1194
1195                         case "System.Int32":
1196                                 return "&" + name;
1197
1198                         default:
1199                         return "&" + name;
1200                 }
1201         }
1202
1203         static string NsToC (string s)
1204         {
1205                 if (s == null)
1206                         return "";
1207
1208                 s = s.Replace ('.', '_');
1209                 return CamelToC (s);
1210         }
1211
1212         static string NsToFlat (string s)
1213         {
1214                 if (s == null)
1215                         return "";
1216
1217                 s = s.Replace (".", "");
1218                 return s;
1219         }
1220
1221         static string CamelToC (string s)
1222         {
1223                 //converts camel case to c-style
1224
1225                 string o = "";
1226
1227                 bool prev_is_cap = true;
1228
1229                 foreach  (char c in s) {
1230                         char cl = c.ToString ().ToLower ()[0];
1231                         bool is_cap = c != cl;
1232
1233                         if (!prev_is_cap && is_cap) {
1234                                 o += "_";
1235                         }
1236
1237                         o += cl;
1238                         prev_is_cap = is_cap;
1239
1240                         if (c == '_')
1241                                 prev_is_cap = true;
1242                 }
1243
1244                 return o;
1245         }
1246 }
1247
1248 class CodeWriter
1249 {
1250         private StreamWriter w;
1251
1252         public CodeWriter (string fname)
1253         {
1254                 Init (fname);
1255         }
1256
1257         public bool IsDuplicate = false;
1258
1259         void Init (string fname)
1260         {
1261                 if (File.Exists (fname)) {
1262                         string newfname = fname + ".x";
1263                         //Console.WriteLine ("Warning: File " + fname + " already exists, using " + newfname);
1264                         IsDuplicate = true;
1265                         Init (newfname);
1266                         return;
1267                 }
1268
1269                 FileStream fs = new FileStream (fname, FileMode.OpenOrCreate, FileAccess.Write);
1270                 w = new StreamWriter (fs);
1271         }
1272
1273         public string Indenter = "  ";
1274         string cur_indent = "";
1275         int level = 0;
1276
1277         public void Indent ()
1278         {
1279                 level++;
1280                 cur_indent = "";
1281                 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1282         }
1283
1284         public void Outdent ()
1285         {
1286                 level--;
1287                 cur_indent = "";
1288                 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1289         }
1290
1291         public void Write (string text)
1292         {
1293                 w.Write (text);
1294         }
1295
1296         public void WriteLine (string text)
1297         {
1298                 WriteLine (text, true);
1299         }
1300
1301         public void WriteLine (string text, bool autoindent)
1302         {
1303                 char[] opentags = {'{', '('};
1304                 char[] closetags = {'}', ')'};
1305
1306                 if (autoindent && text.TrimStart (closetags) != text)
1307                         Outdent ();
1308
1309                 w.Write (cur_indent);
1310                 w.WriteLine (text);
1311
1312                 if (autoindent && text.TrimEnd (opentags) != text)
1313                         Indent ();
1314         }
1315
1316         public void WriteLine (string text, CodeWriter cc)
1317         {
1318                 WriteLine (text, "", cc, "");
1319         }
1320
1321         public void WriteLine (string text, CodeWriter cc, string suffix)
1322         {
1323                 WriteLine (text, "", cc, suffix);
1324         }
1325
1326         public void WriteLine (string text, string prefix, CodeWriter cc)
1327         {
1328                 WriteLine (text, prefix, cc, "");
1329         }
1330
1331         public void WriteLine (string text, string prefix, CodeWriter cc, string suffix)
1332         {
1333                 WriteLine (text);
1334                 cc.WriteLine (prefix + text + suffix);
1335         }
1336
1337         public void WriteComment (string text)
1338         {
1339                 w.WriteLine ("/* " + text + " */");
1340         }
1341
1342         public void WriteLine ()
1343         {
1344                 w.WriteLine ("");
1345         }
1346
1347         public void Close ()
1348         {
1349                 w.Flush ();
1350                 w.Close ();
1351         }
1352 }