Update build dependencies and add a copy of the gtk-sharp parser from trunk
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 28 May 2009 08:19:19 +0000 (10:19 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 28 May 2009 08:19:19 +0000 (10:19 +0200)
This enables us to build with gtk-sharp 2.12.9.90 instead of current
trunk (which will be released together with GNOME 3.0 or similar).

configure.ac
parser/Makefile.am
parser/gapi-parser.cs [new file with mode: 0644]
parser/gapi2xml.pl [new file with mode: 0755]
parser/gapi_pp.pl [new file with mode: 0755]
source/Makefile.am

index 038944c9dbe02bea5918f7522d07832a8d91fdd6..24d1f32afea1a8fcc5fcecc287da66209b3230c4 100644 (file)
@@ -1,7 +1,7 @@
 AC_INIT(README)
 AC_CANONICAL_SYSTEM
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(gstreamer-sharp, 0.9.5.99)
+AM_INIT_AUTOMAKE(gstreamer-sharp, 0.9.0)
 AM_MAINTAINER_MODE
 
 AC_PROG_CC
@@ -11,7 +11,7 @@ AC_HEADER_STDC
 PACKAGE_VERSION=gstreamer-sharp-0.10
 AC_SUBST(PACKAGE_VERSION)
 
-MONO_REQUIRED_VERSION=1.1
+MONO_REQUIRED_VERSION=2.4
 AC_SUBST(MONO_REQUIRED_VERSION)
 PKG_CHECK_MODULES(MONO_DEPENDENCY, mono >= $MONO_REQUIRED_VERSION, has_mono=true, has_mono=false)
 
@@ -87,7 +87,7 @@ MONODOC_SOURCESDIR=`pkg-config --variable=sourcesdir monodoc`
 AC_SUBST(MONODOC_SOURCESDIR)
 
 ## Versions of dependencies
-GLIBSHARP_REQUIRED_VERSION=2.13.90
+GLIBSHARP_REQUIRED_VERSION=2.12.9.90
 PKG_CHECK_MODULES(GLIBSHARP, glib-sharp-2.0 >= $GLIBSHARP_REQUIRED_VERSION)
 AC_SUBST(GLIBSHARP_LIBS)
 
@@ -99,8 +99,8 @@ PKG_CHECK_MODULES(GLIB,
 AC_SUBST(GLIB_LIBS)
 AC_SUBST(GLIB_CFLAGS)
 
-GSTREAMER_REQUIRED_VERSION=0.10.22.1
-GSTREAMER_PLUGINS_REQUIRED_VERSION=0.10.22.1
+GSTREAMER_REQUIRED_VERSION=0.10.23
+GSTREAMER_PLUGINS_REQUIRED_VERSION=0.10.23
 AC_SUBST(GSTREAMER_REQUIRED_VERSION)
 AC_SUBST(GSTREAMER_PLUGINS_REQUIRED_VERSION)
 PKG_CHECK_MODULES(GST,
@@ -123,11 +123,6 @@ AM_CONDITIONAL(ENABLE_MONODOC, test "x$enable_monodoc" = "xyes")
 ## Check for the gapi programs
 PKG_CHECK_MODULES(GAPI, gapi-2.0 >= $GLIBSHARP_REQUIRED_VERSION)
 
-AC_PATH_PROG(GAPI_PARSER, gapi2-parser, no)
-if test "x$GAPI_PARSER" = "xno"; then
-        AC_MSG_ERROR([You need to install gtk-sharp-gapi])
-fi
-
 PKG_CHECK_MODULES(MONO_NUNIT, mono-nunit >= 1.0, do_tests="yes", do_tests="no")
 AC_SUBST(MONO_NUNIT_LIBS)
 AM_CONDITIONAL(ENABLE_TESTS, test "x$do_tests" = "xyes")
index d7f6acbbbc1698a7fa87d1fe7c98f2295f537b86..1a6c9b695da02f110b83d981801349390a5b3b93 100644 (file)
@@ -1,4 +1,4 @@
-TARGETS = gst-gapi-fixup.exe gst-generate-tags.exe
+TARGETS = gst-gapi-fixup.exe gst-generate-tags.exe gst-gapi-parser.exe
 DEBUGS = $(addsuffix .mdb, $(TARGETS))
 
 all: $(TARGETS)
@@ -9,13 +9,18 @@ gst-gapi-fixup.exe: $(srcdir)/gst-gapi-fixup.cs
 gst-generate-tags.exe: $(srcdir)/gst-generate-tags.cs
        $(CSC) -out:$@ $(srcdir)/gst-generate-tags.cs
 
+gst-gapi-parser.exe: $(srcdir)/gapi-parser.cs
+       $(CSC) -out:$@ $(srcdir)/gapi-parser.cs
 
-noinst_SCRIPTS = $(TARGETS)
+
+noinst_SCRIPTS = $(TARGETS) gapi_pp.pl gapi2xml.pl
 
 CLEANFILES = $(TARGETS) $(DEBUGS)
 MAINTAINERCLEANFILES = Makefile.in
 
 EXTRA_DIST = \
        gst-gapi-fixup.cs \
-       gst-generate-tags.cs
+       gst-generate-tags.cs \
+       gapi_pp.pl \
+       gapi2xml.pl
 
diff --git a/parser/gapi-parser.cs b/parser/gapi-parser.cs
new file mode 100644 (file)
index 0000000..56ba5c5
--- /dev/null
@@ -0,0 +1,170 @@
+// gapi-parser.cs - parsing driver application.\r
+//\r
+// Author: Mike Kestner <mkestner@novell.com>\r
+//\r
+// Copyright (c) 2005 Novell, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of version 2 of the GNU General Public\r
+// License as published by the Free Software Foundation.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+// General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public\r
+// License along with this program; if not, write to the\r
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
+// Boston, MA 02111-1307, USA.\r
+\r
+namespace GtkSharp.Parsing {\r
+\r
+       using System;\r
+       using System.Collections;\r
+       using System.IO;\r
+       using System.Runtime.InteropServices;\r
+       using System.Xml;\r
+\r
+       public class Parser  {\r
+\r
+               [DllImport ("libc")]\r
+               static extern int system (string command);\r
+\r
+               public static int Main (string[] args)\r
+               {\r
+                       if (args.Length != 1) {\r
+                               Console.WriteLine ("Usage: gapi2-parser <filename>");\r
+                               return 0;\r
+                       }\r
+\r
+                       XmlDocument src_doc = new XmlDocument ();\r
+\r
+                       try {\r
+                               using (Stream stream = File.OpenRead (args [0]))\r
+                                       src_doc.Load (stream);\r
+                       } catch (XmlException e) {\r
+                               Console.WriteLine ("Couldn't open source file.");\r
+                               Console.WriteLine (e);\r
+                               return 1;\r
+                       }\r
+\r
+                       XmlNode root = src_doc.DocumentElement;\r
+                       if (root.Name != "gapi-parser-input") {\r
+                               Console.WriteLine ("Improperly formatted input file: " + args [0]);\r
+                               return 1;\r
+                       }\r
+\r
+                       foreach (XmlNode apinode in root.ChildNodes) {\r
+                               if (apinode.Name != "api")\r
+                                       continue;\r
+\r
+                               string outfile = (apinode as XmlElement).GetAttribute ("filename");\r
+                               string prefile = outfile + ".pre";\r
+\r
+                               if (File.Exists (prefile))\r
+                                       File.Delete (prefile);\r
+\r
+                               foreach (XmlNode libnode in apinode.ChildNodes) {\r
+                                       if (libnode.Name != "library")\r
+                                               continue;\r
+\r
+                                       string lib = (libnode as XmlElement).GetAttribute ("name");\r
+                       \r
+                                       foreach (XmlNode nsnode in libnode.ChildNodes) {\r
+                                               if (nsnode.Name != "namespace")\r
+                                                       continue;\r
+\r
+                                               string ns = (nsnode as XmlElement).GetAttribute ("name");\r
+                       \r
+                                               ArrayList files = new ArrayList ();\r
+                                               Hashtable excludes = new Hashtable ();\r
+\r
+                                               foreach (XmlNode srcnode in nsnode.ChildNodes) {\r
+                                                       if (!(srcnode is XmlElement))\r
+                                                               continue;\r
+\r
+                                                       XmlElement elem = srcnode as XmlElement;\r
+\r
+                                                       switch (srcnode.Name) {\r
+                                                       case "dir":\r
+                                                               string dir = elem.InnerXml;\r
+                                                               Console.Write ("<dir {0}> ", dir);\r
+                                                               DirectoryInfo di = new DirectoryInfo (dir);\r
+                                                               foreach (FileInfo file in di.GetFiles ("*.c"))\r
+                                                                       files.Add (dir + Path.DirectorySeparatorChar + file.Name);\r
+                                                               foreach (FileInfo file in di.GetFiles ("*.h"))\r
+                                                                       files.Add (dir + Path.DirectorySeparatorChar + file.Name);\r
+                                                               break;\r
+                                                       case "file":\r
+                                                               string incfile = elem.InnerXml;\r
+                                                               Console.Write ("<file {0}> ", incfile);\r
+                                                               files.Add (incfile);\r
+                                                               break;\r
+                                                       case "exclude":\r
+                                                               string excfile = elem.InnerXml;\r
+                                                               Console.Write ("<exclude {0}> ", excfile);\r
+                                                               excludes [excfile] = 1;\r
+                                                               break;\r
+                                                       case "directory":\r
+                                                               string dir_path = elem.GetAttribute ("path");\r
+                                                               Console.Write ("<directory {0}: excluding ", dir_path);\r
+                                                               Hashtable excs = new Hashtable ();\r
+                                                               foreach (XmlNode exc_node in srcnode.ChildNodes) {\r
+                                                                       if (exc_node.Name != "exclude")\r
+                                                                               continue;\r
+                                                                       string excfilename = (exc_node as XmlElement).InnerXml;\r
+                                                                       Console.Write (excfilename + " ");\r
+                                                                       excs [excfilename] = 1;\r
+                                                               }\r
+                                                               DirectoryInfo dinfo = new DirectoryInfo (dir_path);\r
+                                                               foreach (FileInfo file in dinfo.GetFiles ("*.c")) {\r
+                                                                       if (excs.Contains (file.Name))\r
+                                                                               continue;\r
+                                                                       files.Add (dir_path + Path.DirectorySeparatorChar + file.Name);\r
+                                                               }\r
+                                                               foreach (FileInfo file in dinfo.GetFiles ("*.h")) {\r
+                                                                       if (excs.Contains (file.Name))\r
+                                                                               continue;\r
+                                                                       files.Add (dir_path + Path.DirectorySeparatorChar + file.Name);\r
+                                                               }\r
+                                                               Console.Write ("> ");\r
+                                                               break;\r
+                                                       default:\r
+                                                               Console.WriteLine ("Invalid source: " + srcnode.Name);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+\r
+                                               Console.WriteLine ();\r
+\r
+                                               if (files.Count == 0)\r
+                                                       continue;\r
+\r
+                                               ArrayList realfiles = new ArrayList ();\r
+                                               foreach (string file in files) {\r
+                                                       string trimfile = file.TrimEnd ();\r
+                                                       if (excludes.Contains (trimfile))\r
+                                                               continue;\r
+\r
+                                                       realfiles.Add (trimfile);\r
+                                               }\r
+                                                               \r
+                                               string[] filenames = (string[]) realfiles.ToArray (typeof (string));\r
+                                               string pp_args = String.Join (" ", filenames);\r
+                                               system ("gapi_pp.pl " + pp_args + " | gapi2xml.pl " + ns + " " + prefile + " " + lib);\r
+                                       }\r
+                               }\r
+                       \r
+                               XmlDocument final = new XmlDocument ();\r
+                               final.Load (prefile);\r
+                               XmlTextWriter writer = new XmlTextWriter (outfile, null);\r
+                               writer.Formatting = Formatting.Indented;\r
+                               final.Save (writer);\r
+                               File.Delete (prefile);\r
+                       }\r
+\r
+                       return 0;\r
+               }\r
+       }\r
+}\r
diff --git a/parser/gapi2xml.pl b/parser/gapi2xml.pl
new file mode 100755 (executable)
index 0000000..b5d4cc0
--- /dev/null
@@ -0,0 +1,1197 @@
+#!/usr/bin/perl
+#
+# gapi2xml.pl : Generates an XML representation of GObject based APIs.
+#
+# Author: Mike Kestner <mkestner@speakeasy.net>
+#
+# Copyright (c) 2001-2003 Mike Kestner
+# Copyright (c) 2003-2009 Novell, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+##############################################################
+
+$parser_version = 2;
+$debug=$ENV{'GAPI_DEBUG'};
+
+use XML::LibXML;
+
+if (!$ARGV[2]) {
+       die "Usage: gapi_pp.pl <srcdir> | gapi2xml.pl <namespace> <outfile> <libname>\n";
+}
+
+$ns = $ARGV[0];
+$libname = $ARGV[2];
+
+# Used by name mangling sub
+%num2txt = ('1', "One", '2', "Two", '3', "Three", '4', "Four", '5', "Five",
+           '6', "Six", '7', "Seven", '8', "Eight", '9', "Nine", '0', "Zero");
+
+##############################################################
+# Check if the filename provided exists.  We parse existing files into
+# a tree and append the namespace to the root node.  If the file doesn't 
+# exist, we create a doc tree and root node to work with.
+##############################################################
+
+if (-e $ARGV[1]) {
+       #parse existing file and get root node.
+       $doc = XML::LibXML->new->parse_file($ARGV[1]);
+       $root = $doc->getDocumentElement();
+       if ($root->getAttribute ('parser_version') != $parser_version) {
+               die "The version of the file does not match the version of the parser";
+       }
+} else {
+       $doc = XML::LibXML::Document->new();
+       $root = $doc->createElement('api');
+       $root->setAttribute('parser_version', $parser_version);
+       $doc->setDocumentElement($root);
+       $warning_node = XML::LibXML::Comment->new ("\n\n        This file was automatically generated.\n        Please DO NOT MODIFY THIS FILE, modify .metadata files instead.\n\n");
+       $root->appendChild($warning_node);
+}
+
+$ns_elem = $doc->createElement('namespace');
+$ns_elem->setAttribute('name', $ns);
+$ns_elem->setAttribute('library', $libname);
+$root->appendChild($ns_elem);
+
+##############################################################
+# First we parse the input for typedefs, structs, enums, and class_init funcs
+# and put them into temporary hashes.
+##############################################################
+
+while ($line = <STDIN>) {
+       if ($line =~ /typedef\s+(struct\s+\w+\s+)\*+(\w+);/) {
+               $ptrs{$2} = $1;
+       } elsif ($line =~ /typedef\s+(struct\s+\w+)\s+(\w+);/) {
+               next if ($2 =~ /Private$/);
+               # fixme: siiigh
+               $2 = "GdkDrawable" if ($1 eq "_GdkDrawable");
+               $types{$2} = $1;
+       } elsif ($line =~ /typedef\s+struct/) {
+               $sdef = $line;
+               while ($line = <STDIN>) {
+                       $sdef .= $line;
+                       last if ($line =~ /^(deprecated)?}/);
+               }
+               $sdef =~ s!/\*.*?(\*/|\n)!!g;
+               $sdef =~ s/\n\s*//g;
+               $types{$1} = $sdef if ($sdef =~ /.*\}\s*(\w+);/);
+       } elsif ($line =~ /typedef\s+(unsigned\s+\w+)\s+(\**)(\w+);/) {
+               $types{$3} = $1 . $2;
+       } elsif ($line =~ /typedef\s+(\w+)\s+(\**)(\w+);/) {
+               $types{$3} = $1 . $2;
+       } elsif ($line =~ /typedef\s+enum\s+(\w+)\s+(\w+);/) {
+               $etypes{$1} = $2;
+       } elsif ($line =~ /^((deprecated)?typedef\s+)?\benum\b/) {
+               $edef = $line;
+               while ($line = <STDIN>) {
+                       $edef .= $line;
+                       last if ($line =~ /^(deprecated)?}\s*(\w+)?;/);
+               }
+               $edef =~ s/\n\s*//g;
+               $edef =~ s|/\*.*?\*/||g;
+               if ($edef =~ /typedef.*}\s*(\w+);/) {
+                       $ename = $1;
+               } elsif ($edef =~ /^(deprecated)?enum\s+(\w+)\s*{/) {
+                       $ename = $2;
+               } else {
+                       print "Unexpected enum format\n$edef";
+                       next;
+               }
+               $edefs{$ename} = $edef;
+       } elsif ($line =~ /typedef\s+\w+\s*\**\s*\(\*\s*(\w+)\)\s*\(/) {
+               $fname = $1;
+               $fdef = "";
+               while ($line !~ /;/) {
+                       $fdef .= $line;
+                       $line = <STDIN>;
+               }
+               $fdef .= $line;
+               $fdef =~ s/\n\s+//g;
+               $fpdefs{$fname} = $fdef;
+       } elsif ($line =~ /^(private|deprecated)?struct\s+(\w+)/) {
+               next if ($line =~ /;/);
+               $sname = $2;
+               $sdef = $line;
+               while ($line = <STDIN>) {
+                       $sdef .= $line;
+                       last if ($line =~ /^(deprecated)?}/);
+               }
+               $sdef =~ s!/\*[^<].*?(\*/|\n)!!g;
+               $sdef =~ s/\n\s*//g;
+               $sdefs{$sname} = $sdef if (!exists ($sdefs{$sname}));
+       } elsif ($line =~ /^(\w+)_(class|base)_init\b/) {
+               $class = StudlyCaps($1);
+               $pedef = $line;
+               while ($line = <STDIN>) {
+                       $pedef .= $line;
+                       last if ($line =~ /^(deprecated)?}/);
+               }
+               $pedefs{lc($class)} = $pedef;
+       } elsif ($line =~ /^(\w+)_get_type\b/) {
+               $class = StudlyCaps($1);
+               $pedef = $line;
+               while ($line = <STDIN>) {
+                       $pedef .= $line;
+                       if ($line =~ /g_boxed_type_register_static/) {
+                               $boxdef = $line;
+                               while ($line !~ /;/) {
+                                       $boxdef .= ($line = <STDIN>);
+                               }
+                               $boxdef =~ s/\n\s*//g;
+                               $boxdef =~ /\(\"(\w+)\"/;
+                               my $boxtype = $1;
+                               $boxtype =~ s/($ns)Type(\w+)/$ns$2/;
+                               $boxdefs{$boxtype} = $boxdef;
+                       } elsif ($line =~ /g_(enum|flags)_register_static/) {
+                               $pedef =~ /^(\w+_get_type)/;
+                               $enum_gtype{$class} = $1;
+                       }
+                       last if ($line =~ /^(deprecated)?}/);
+               }
+               $typefuncs{lc($class)} = $pedef;
+       } elsif ($line =~ /^G_DEFINE_TYPE_WITH_CODE\s*\(\s*(\w+)/) {
+               $typefuncs{lc($1)} = $line;
+       } elsif ($line =~ /^(deprecated)?(const|G_CONST_RETURN)?\s*(struct\s+)?\w+\s*\**(\s*(const|G_CONST_RETURN)\s*\**)?\s*(\w+)\s*\(/) {
+               $fname = $6;
+               $fdef = "";
+               while ($line !~ /;/) {
+                       $fdef .= $line;
+                       $line = <STDIN>;
+               }
+               $fdef .= $line;
+               $fdef =~ s/\n\s*//g;
+               if ($fdef !~ /^_/) {
+                       $fdefs{$fname} = $fdef;
+               }
+       } elsif ($line =~ /CHECK_(\w*)CAST/) {
+               $cast_macro = $line;
+               while ($line =~ /\\$/) {
+                       $line = <STDIN>;
+                       $cast_macro .= $line;
+               }
+               $cast_macro =~ s/\\\n\s*//g;
+               $cast_macro =~ s/\s+/ /g;
+               if ($cast_macro =~ /G_TYPE_CHECK_(\w+)_CAST.*,\s*(\w+),\s*(\w+)\)/) {
+                       if ($1 eq "INSTANCE") {
+                               $objects{$2} = $3 . $objects{$2};
+                       } else {
+                               $objects{$2} .= ":$3";
+                       }
+               } elsif ($cast_macro =~ /G_TYPE_CHECK_(\w+)_CAST.*,\s*([a-zA-Z0-9]+)_(\w+)_get_type\s*\(\),\s*(\w+)\)/) {
+                       $typename = uc ("$2_type_$3");
+                       if ($1 eq "INSTANCE") {
+                               $objects{$typename} = $4 . $objects{$typename};
+                       } else {
+                               $objects{$typename} .= ":$4";
+                       }
+               } elsif ($cast_macro =~ /GTK_CHECK_CAST.*,\s*(\w+),\s*(\w+)/) {
+                       $objects{$1} = $2 . $objects{$1};
+               } elsif ($cast_macro =~ /GTK_CHECK_CLASS_CAST.*,\s*(\w+),\s*(\w+)/) {
+                       $objects{$1} .= ":$2";
+               } elsif ($cast_macro =~ /GST_IMPLEMENTS_INTERFACE_CHECK_INSTANCE_CAST.*,\s*(\w+),\s*(\w+)/) {
+                       $objects{$1} = $2 . $objects{$1};
+               }
+       } elsif ($line =~ /INSTANCE_GET_INTERFACE.*,\s*(\w+),\s*(\w+)/) {
+               $ifaces{$1} = $2;
+       } elsif ($line =~ /^BUILTIN\s*\{\s*\"(\w+)\".*GTK_TYPE_BOXED/) {
+               $boxdefs{$1} = $line;
+       } elsif ($line =~ /^BUILTIN\s*\{\s*\"(\w+)\".*GTK_TYPE_(ENUM|FLAGS)/) {
+               # ignoring these for now.
+       } elsif ($line =~ /^(deprecated)?\#define/) {
+               my $test_ns = uc ($ns);
+               if ($line =~ /^deprecated\#define\s+(\w+)\s+\"(.*)\"/) {
+                       $defines{"deprecated$1"} = $2;
+               } elsif ($line =~ /\#define\s+(\w+)\s+\"(.*)\"/) {
+                       $defines{$1} = $2;
+               }
+       } elsif ($line !~ /\/\*/) {
+               print $line;
+       }
+}
+
+##############################################################
+# Produce the enum definitions.
+##############################################################
+%enums = ();
+
+foreach $cname (sort(keys(%edefs))) {
+       $ecnt++;
+       $def = $edefs{$cname};
+       $cname = $etypes{$cname} if (exists($etypes{$cname}));
+       $enums{lc($cname)} = $cname;
+       $enum_elem = addNameElem($ns_elem, 'enum', $cname, $ns);
+       if ($def =~ /^deprecated/) {
+               $enum_elem->setAttribute("deprecated", "1");
+               $def =~ s/deprecated//g;
+       }
+       if ($enum_gtype{$cname}) {
+               $enum_elem->setAttribute("gtype", $enum_gtype{$cname});
+       }
+       if ($def =~ /<</) {
+               $enum_elem->setAttribute('type', "flags");
+       } else {
+               $enum_elem->setAttribute('type', "enum");
+       }
+       $def =~ /\{(.*\S)\s*\}/;
+       @vals = split(/,\s*/, $1);
+       $vals[0] =~ s/^\s+//;
+       @nameandval = split(/=/, $vals[0]);
+       @v0 = split(/_/, $nameandval[0]);
+       if (@vals > 1) {
+               $done = 0;
+               for ($idx = 0, $regex = ""; $idx < @v0; $idx++) {
+                       $regex .= ($v0[$idx] . "_");
+                       foreach $val (@vals) {
+                               $done = 1 if ($val !~ /$regex/);
+                       }
+                       last if $done;
+               }
+               $common = join("_", @v0[0..$idx-1]);
+       } else {
+               $common = join("_", @v0[0..$#v0-1]);
+       }
+       
+       foreach $val (@vals) {
+               $val =~ s/=\s*\(\s*(.*\S)\s*\)\s*/= \1/;
+               if ($val =~ /$common\_?(\w+)\s*=\s*(.*)$/) {
+                       $name = $1;
+                       $enumval = $2;
+                       if ($enumval =~ /^(\d+|0x[0-9A-Fa-f]+)u?\s*<<\s*(\d+)$/) {
+                               $enumval = "$1 << $2";
+                       } elsif ($enumval =~ /^$common\_?(\w+)$/) {
+                               $enumval = StudlyCaps(lc($1))
+                       }
+               } elsif ($val =~ /$common\_?(\w+)/) {
+                       $name = $1; $enumval = "";
+               } else {
+                       die "Unexpected enum value: $val for common value $common\n";
+               }
+
+               $val_elem = addNameElem($enum_elem, 'member');
+               $val_elem->setAttribute('cname', "$common\_$name");
+               $val_elem->setAttribute('name', StudlyCaps(lc($name)));
+               if ($enumval) {
+                       $val_elem->setAttribute('value', $enumval);
+               }
+       }
+}
+
+##############################################################
+# Parse the callbacks.
+##############################################################
+
+foreach $cbname (sort(keys(%fpdefs))) {
+       next if ($cbname =~ /^_/);
+       $cbcnt++;
+       $fdef = $cb = $fpdefs{$cbname};
+       $cb_elem = addNameElem($ns_elem, 'callback', $cbname, $ns);
+       $cb =~ /typedef\s+(.*)\(.*\).*\((.*)\);/;
+       $ret = $1; $params = $2;
+       addReturnElem($cb_elem, $ret);
+       if ($params && ($params ne "void")) {
+               addParamsElem($cb_elem, split(/,/, $params));
+       }
+}
+
+##############################################################
+# Parse the interfaces list.   
+##############################################################
+
+foreach $type (sort(keys(%ifaces))) {
+
+       $iface = $ifaces{$type};
+       ($inst, $dontcare) = split(/:/, delete $objects{$type});
+       $initfunc = $pedefs{lc($inst)};
+       $ifacetype = delete $types{$iface};
+       delete $types{$inst};
+       
+       $ifacecnt++;
+       $iface_el = addNameElem($ns_elem, 'interface', $inst, $ns);
+
+       $elem_table{lc($inst)} = $iface_el;
+
+       $classdef = $sdefs{$1} if ($ifacetype =~ /struct\s+(\w+)/);
+       my @signal_vms;
+       if ($initfunc) {
+               @signal_vms = parseInitFunc($iface_el, $initfunc, $classdef);
+       } else {
+               warn "Don't have an init func for $inst.\n" if $debug;
+               # my @signal_vms;
+       }
+
+       addClassElem ($iface_el, $classdef, @signal_vms) if ($classdef);
+}
+
+
+##############################################################
+# Parse the classes by walking the objects list.   
+##############################################################
+
+foreach $type (sort(keys(%objects))) {
+       ($inst, $class) = split(/:/, $objects{$type});
+       $class = $inst . "Class" if (!$class);
+       $initfunc = $pedefs{lc($inst)};
+       $typefunc = $typefuncs{lc($inst)};
+       $insttype = delete $types{$inst};
+       $classtype = delete $types{$class};
+
+       $instdef = $classdef = "";
+       $instdef = $sdefs{$1} if ($insttype =~ /struct\s+(\w+)/);
+       $classdef = $sdefs{$1} if ($classtype =~ /struct\s+(\w+)/);
+       $classdef =~ s/deprecated//g;
+       $instdef =~ s/\s+(\*+)([^\/])/\1 \2/g;
+       warn "Strange Class $inst\n" if (!$instdef && $debug);
+
+       $classcnt++;
+       $obj_el = addNameElem($ns_elem, 'object', $inst, $ns);
+
+       $elem_table{lc($inst)} = $obj_el;
+
+       # Check if the object is deprecated
+       if ($instdef =~ /^deprecatedstruct/) {
+               $obj_el->setAttribute("deprecated", "1");
+               $instdef =~ s/deprecated//g;
+       }
+       
+       # Extract parent and fields from the struct
+       if ($instdef =~ /^struct/) {
+               $instdef =~ /\{(.*)\}/;
+               $fieldstr = $1;
+               $fieldstr =~ s|/\*[^<].*?\*/||g;
+               @fields = split(/;/, $fieldstr);
+               addFieldElems($obj_el, 'private', @fields);
+               $obj_el->setAttribute('parent', $obj_el->firstChild->getAttribute('type'));
+               $obj_el->removeChild($obj_el->firstChild);
+       } elsif ($instdef =~ /privatestruct/) {
+               # just get the parent for private structs
+               $instdef =~ /\{\s*(\w+)/;
+               $obj_el->setAttribute('parent', "$1");
+       }
+
+       # Get the props from the class_init func.
+       if ($initfunc) {
+               @signal_vms = parseInitFunc($obj_el, $initfunc, $classdef);
+       } else {
+               warn "Don't have an init func for $inst.\n" if $debug;
+       }
+
+       addClassElem ($obj_el, $classdef, @signal_vms) if ($classdef);
+
+       # Get the interfaces from the class_init func.
+       if ($typefunc) {
+               if ($typefunc =~ /G_DEFINE_TYPE_WITH_CODE/) {
+                       parseTypeFuncMacro($obj_el, $typefunc);
+               } else {
+                       parseTypeFunc($obj_el, $typefunc);
+               }
+       } else {
+               warn "Don't have a GetType func for $inst.\n" if $debug;
+       }
+
+}
+
+##############################################################
+# Parse the remaining types.
+##############################################################
+
+foreach $key (sort (keys (%types))) {
+
+       $lasttype = $type = $key;
+       while ($type && ($types{$type} !~ /struct/)) {
+               $lasttype = $type;
+               $type = $types{$type};
+       }
+
+       if ($types{$type} =~ /struct\s+(\w+)/) {
+               $type = $1;
+               if (exists($sdefs{$type})) {
+                       $def = $sdefs{$type};
+               } else {
+                       $def = "privatestruct";
+               }
+       } elsif ($types{$type} =~ /struct/ && $type =~ /^$ns/) {
+               $def = $types{$type};
+       } else {
+               $elem = addNameElem($ns_elem, 'alias', $key, $ns);
+               $elem->setAttribute('type', $lasttype);
+               warn "alias $key to $lasttype\n" if $debug;
+               next;
+       }
+
+       # fixme: hack
+       if ($key eq "GdkBitmap") {
+               $struct_el = addNameElem($ns_elem, 'object', $key, $ns);
+       } elsif (exists($boxdefs{$key})) {
+               $struct_el = addNameElem($ns_elem, 'boxed', $key, $ns);
+       } else {
+               $struct_el = addNameElem($ns_elem, 'struct', $key, $ns);
+       }
+
+       if ($def =~ /^deprecated/) {
+               $struct_el->setAttribute("deprecated", "1");
+               $def =~ s/deprecated//g;
+       }
+
+       $elem_table{lc($key)} = $struct_el;
+
+       $def =~ s/\s+/ /g;
+       if ($def =~ /privatestruct/) {
+               $struct_el->setAttribute('opaque', 'true');
+       } else {
+               $def =~ /\{(.+)\}/;
+               addFieldElems($struct_el, 'public', split(/;/, $1));
+       }
+}
+
+# really, _really_ opaque structs that aren't even defined in sources. Lovely.
+foreach $key (sort (keys (%ptrs))) {
+       next if $ptrs{$key} !~ /struct\s+(\w+)/;
+       $type = $1;
+       $struct_el = addNameElem ($ns_elem, 'struct', $key, $ns);
+       $struct_el->setAttribute('opaque', 'true');
+       $elem_table{lc($key)} = $struct_el;
+}
+
+addFuncElems();
+addStaticFuncElems();
+
+# This should probably be done in a more generic way
+foreach $define (sort (keys (%defines))) {
+       next if $define !~ /[A-Z]_STOCK_/;
+       if ($stocks{$ns}) {
+               $stock_el = $stocks{$ns};
+       } else {
+               $stock_el = addNameElem($ns_elem, "object", $ns . "Stock", $ns);
+               $stocks{$ns} = $stock_el;
+       }
+       $string_el = addNameElem ($stock_el, "static-string", $define);
+       $string_name = lc($define);
+       $string_name =~ s/\w+_stock_//;
+       $string_el->setAttribute('name', StudlyCaps($string_name));
+       $string_el->setAttribute('value', $defines{$define});
+}
+
+##############################################################
+# Output the tree
+##############################################################
+
+if ($ARGV[1]) {
+       open(XMLFILE, ">$ARGV[1]") || die "Couldn't open $ARGV[1] for writing.\n";
+       print XMLFILE $doc->toString();
+       close(XMLFILE);
+} else {
+       print $doc->toString();
+}
+
+##############################################################
+# Generate a few stats from the parsed source.
+##############################################################
+
+$scnt = keys(%sdefs); $fcnt = keys(%fdefs); $tcnt = keys(%types);
+print "structs: $scnt  enums: $ecnt  callbacks: $cbcnt\n";
+print "funcs: $fcnt types: $tcnt  classes: $classcnt\n";
+print "props: $propcnt childprops: $childpropcnt signals: $sigcnt\n\n";
+
+sub addClassElem
+{
+       my ($obj_el, $classdef, @signal_vms) = @_;
+
+       my %is_signal_vm;
+       for (@signal_vms) { 
+               $is_signal_vm{$_} = 1;
+       }
+
+       if ($classdef =~ /struct\s+_?(\w+)\s*{(.*)};/) {
+               my $elem = $doc->createElement('class_struct');
+               $elem->setAttribute('cname', $1);
+               $obj_el->insertBefore($elem, $obj_el->firstChild);
+               $fields = $2;
+               $fields =~ s!/\*.*?\*/!!g; # Remove comments
+               foreach $field (split (/;/, $fields)) {
+                       if ($field =~ /\s*(G_CONST_RETURN\s+)?(\S+\s*\**)\s*\(\s*\*\s*(\w+)\)\s*(\((.*?)\))?/) {
+                               $ret = $1 . $2; $cname = $3; $parms = $5;
+
+                               $class_elem = $doc->createElement('method');
+                               $elem->appendChild($class_elem);
+
+                               if ($is_signal_vm{$cname}) {
+                                       $class_elem->setAttribute('signal_vm', $cname);
+                               } else {
+                                       $class_elem->setAttribute('vm', $cname);
+
+                                       $vm_elem = $doc->createElement('virtual_method');
+                                       $obj_el->appendChild($vm_elem);
+                                       $vm_elem->setAttribute('name', StudlyCaps($cname));
+                                       $vm_elem->setAttribute('cname', $cname);
+
+                                       addReturnElem($vm_elem, $ret);
+
+                                       if ($parms && ($parms ne "void")) { # if there are any parameters
+                                               @parm_arr = split(/,/, $parms);
+                                               $parms =~ /\s*(\w+)/; # Get type of first parameter
+                                               if ($1 ne $obj_el->getAttribute ('cname')) {
+                                                       $vm_elem->setAttribute('shared', 'true'); # First parameter is not of the type of the declaring class -> static vm
+                                               } else {
+                                                       ($dump, @parm_arr) = @parm_arr;
+                                               }
+                                               addParamsElem($vm_elem, @parm_arr);
+                                       } else {
+                                               $vm_elem->setAttribute('shared', 'true');
+                                       }
+
+                                       if ($cname =~ /reserved[0-9]+$/ || $cname =~ /padding[0-9]+$/ || $cname =~ /recent[0-9]+$/) {
+                                               $vm_elem->setAttribute('padding', 'true');
+                                       }
+                               }
+                       } elsif ($field =~ /(unsigned\s+)?(\S+)\s+(.+)/) {
+                               my $type = $1 . $2; $symb = $3;
+                               foreach $tok (split (/,\s*/, $symb)) { # multiple field defs may occur in one line; like int xrange, yrange;
+                                       $tok =~ /(\*)?(\w+)\s*(.*)/;
+                                       my $field_type = $type . $1; my $cname = $2; my $modifiers = $3;
+
+                                       $fld_elem = addNameElem($elem, 'field', $cname, "");
+                                       $fld_elem->setAttribute('type', "$field_type");
+
+                                       if ($modifiers =~ /\[(.*)\]/) {
+                                               $fld_elem->setAttribute('array_len', "$1");
+                                       } elsif ($modifiers =~ /\:\s*(\d+)/) {
+                                               $fld_elem->setAttribute('bits', "$1");
+                                       }
+                               }
+                       } elsif ($field =~ /\S+/) {
+                               print "***** Unmatched class struct field $field\n";
+                       }
+               }
+       } else {
+               print "***** Unmatched $classdef\n";
+       }
+}
+
+sub addFieldElems
+{
+       my ($parent, $defaultaccess, @fields) = @_;
+       my $access = $defaultaccess;
+
+       foreach $field (@fields) {
+               if ($field =~ m!/\*< (public|private) >.*\*/(.*)$!) {
+                       $access = $1;
+                       $field = $2;
+               }
+               next if ($field !~ /\S/);
+               $field =~ s/GSEAL\s*\((.*)\)/\1/g;
+               $field =~ s/\s+(\*+)/\1 /g;
+               $field =~ s/(const\s+)?(\w+)\*\s+const\*/const \2\*/g;
+               $field =~ s/(\w+)\s+const\s*\*/const \1\*/g;
+               $field =~ s/const /const\-/g;
+               $field =~ s/struct /struct\-/g;
+               $field =~ s/.*\*\///g;
+               next if ($field !~ /\S/);
+               
+               if ($field =~ /(\S+\s+\*?)\(\*\s*(.+)\)\s*\((.*)\)/) {
+                       $elem = addNameElem($parent, 'callback', $2);
+                       addReturnElem($elem, $1);
+                       addParamsElem($elem, $3);
+               } elsif ($field =~ /(unsigned )?(\S+)\s+(.+)/) {
+                       my $type = $1 . $2; $symb = $3;
+                       foreach $tok (split (/,\s*/, $symb)) {
+                               if ($tok =~ /(\w+)\s*\[(.*)\]/) {
+                                       $elem = addNameElem($parent, 'field', $1, "");
+                                       $elem->setAttribute('array_len', "$2");
+                               } elsif ($tok =~ /(\w+)\s*\:\s*(\d+)/) {
+                                       $elem = addNameElem($parent, 'field', $1, "");
+                                       $elem->setAttribute('bits', "$2");
+                               } else {
+                                       $elem = addNameElem($parent, 'field', $tok, "");
+                               }
+                               $elem->setAttribute('type', "$type");
+
+                               if ($access ne $defaultaccess) {
+                                       $elem->setAttribute('access', "$access");
+                               }
+                       }
+               } else {
+                       die "$field\n";
+               }
+       }
+}
+
+sub addFuncElems
+{
+       my ($obj_el, $inst, $prefix);
+
+       $fcnt = keys(%fdefs);
+
+       foreach $mname (sort (keys (%fdefs))) {
+               next if ($mname =~ /^_/);
+               $obj_el = "";
+               $prefix = $mname;
+               $prepend = undef;
+               while ($prefix =~ /(\w+)_/) {
+                       $prefix = $key = $1;
+                       $key =~ s/_//g;
+                       # FIXME: lame Gdk API hack
+                       if ($key eq "gdkdraw") {
+                               $key = "gdkdrawable";
+                               $prepend = "draw_";
+                       }
+                       if (exists ($elem_table{$key})) {
+                               $prefix .= "_";
+                               $obj_el = $elem_table{$key};
+                               $inst = $key;
+                               last;
+                       } elsif (exists ($enums{$key}) && ($mname =~ /_get_type/)) {
+                               delete $fdefs{$mname};
+                               last;
+                       }
+               }
+               next if (!$obj_el);
+
+               $mdef = delete $fdefs{$mname};
+               
+               if ($mname =~ /$prefix(new)/) {
+                       $el = addNameElem($obj_el, 'constructor', $mname);
+                       if ($mdef =~ /^deprecated/) {
+                               $el->setAttribute("deprecated", "1");
+                               $mdef =~ s/deprecated//g;
+                       }
+                       $drop_1st = 0;
+               } else {
+                       $el = addNameElem($obj_el, 'method', $mname, $prefix, $prepend);
+                       if ($mdef =~ /^deprecated/) {
+                               $el->setAttribute("deprecated", "1");
+                               $mdef =~ s/deprecated//g;
+                       }
+                       $mdef =~ /(.*?)\w+\s*\(/;
+                       addReturnElem($el, $1);
+                       $mdef =~ /\(\s*(const)?\s*(\w+)/;
+                       if (lc($2) ne $inst) {
+                               $el->setAttribute("shared", "true");
+                               $drop_1st = 0;
+                       } else {
+                               $drop_1st = 1;
+                       }
+               }
+
+               parseParms ($el, $mdef, $drop_1st);
+
+               # Don't add "free" to this regexp; that will wrongly catch all boxed types
+               if ($mname =~ /$prefix(new|destroy|ref|unref)/ &&
+                   ($obj_el->nodeName eq "boxed" || $obj_el->nodeName eq "struct") &&
+                   $obj_el->getAttribute("opaque") ne "true") {
+                       $obj_el->setAttribute("opaque", "true");
+                       for my $field ($obj_el->getElementsByTagName("field")) {
+                               if (!$field->getAttribute("access")) {
+                                       $field->setAttribute("access", "public");
+                                       $field->setAttribute("writeable", "true");
+                               }
+                       }
+               }
+       }
+}
+
+sub parseParms
+{
+       my ($el, $mdef, $drop_1st) = @_;
+
+       $fmt_args = 0;
+
+       if ($mdef =~ /G_GNUC_PRINTF.*\((\d+,\s*\d+)\s*\)/) {
+               $fmt_args = $1;
+               $mdef =~ s/\s*G_GNUC_PRINTF.*\)//;
+       }
+
+       if (($mdef =~ /\((.*)\)/) && ($1 ne "void")) {
+               @parms = ();
+               $parm = "";
+               $pcnt = 0;
+               foreach $char (split(//, $1)) {
+                       if ($char eq "(") {
+                               $pcnt++;
+                       } elsif ($char eq ")") {
+                               $pcnt--;
+                       } elsif (($pcnt == 0) && ($char eq ",")) {
+                               @parms = (@parms, $parm);
+                               $parm = "";
+                               next;
+                       }
+                       $parm .= $char;
+               }
+
+               if ($parm) {
+                       @parms = (@parms, $parm);
+               }
+               # @parms = split(/,/, $1);
+               ($dump, @parms) = @parms if $drop_1st;
+               if (@parms > 0) {
+                       addParamsElem($el, @parms);
+               }
+
+               if ($fmt_args != 0) {
+                       $fmt_args =~ /(\d+),\s*(\d+)/;
+                       $fmt = $1; $args = $2;
+                       ($params_el, @junk) = $el->getElementsByTagName ("parameters");
+                       (@params) = $params_el->getElementsByTagName ("parameter");
+                       $offset = 1 + $drop_1st;
+                       $params[$fmt-$offset]->setAttribute ("printf_format", "true");
+                       $params[$args-$offset]->setAttribute ("printf_format_args", "true");
+               }
+       }
+}
+
+sub addStaticFuncElems
+{
+       my ($global_el, $ns_prefix);
+
+       @mnames = sort (keys (%fdefs));
+       $mcount = @mnames;
+
+       return if ($mcount == 0);
+
+       $ns_prefix = "";
+       $global_el = "";
+
+       for ($i = 0; $i < $mcount; $i++) {
+               $mname = $mnames[$i];
+               $prefix = $mname;
+               next if ($prefix =~ /^_/);
+
+               if ($ns_prefix eq "") {
+                       my (@toks) = split(/_/, $prefix);
+                       for ($j = 0; $j < @toks; $j++) {
+                               if (join ("", @toks[0 .. $j]) eq lc($ns)) {
+                                       $ns_prefix = join ("_", @toks[0 .. $j]);
+                                       last;
+                               }
+                       }
+                       next if ($ns_prefix eq "");
+               }
+               next if ($mname !~ /^$ns_prefix/);
+
+               if ($mname =~ /($ns_prefix)_([a-zA-Z]+)_\w+/) {
+                       $classname = $2;
+                       $key = $prefix = $1 . "_" . $2 . "_";
+                       $key =~ s/_//g;
+                       $cnt = 1;
+                       if (exists ($enums{$key})) {
+                               $cnt = 1; 
+                       } elsif ($classname ne "set" && $classname ne "get" &&
+                           $classname ne "scan" && $classname ne "find" &&
+                           $classname ne "add" && $classname ne "remove" &&
+                           $classname ne "free" && $classname ne "register" &&
+                           $classname ne "execute" && $classname ne "show" &&
+                           $classname ne "parse" && $classname ne "paint" &&
+                           $classname ne "string") {
+                               while ($mnames[$i+$cnt] =~ /$prefix/) { $cnt++; }
+                       }
+                       if ($cnt == 1) {
+                               $mdef = delete $fdefs{$mname};
+                               
+                               if (!$global_el) {
+                                       $global_el = $doc->createElement('class');
+                                       $global_el->setAttribute('name', "Global");
+                                       $global_el->setAttribute('cname', $ns . "Global");
+                                       $ns_elem->appendChild($global_el);
+                               }
+                               $el = addNameElem($global_el, 'method', $mname, $ns_prefix);
+                               if ($mdef =~ /^deprecated/) {
+                                       $el->setAttribute("deprecated", "1");
+                                       $mdef =~ s/deprecated//g;
+                               }
+                               $mdef =~ /(.*?)\w+\s*\(/;
+                               addReturnElem($el, $1);
+                               $el->setAttribute("shared", "true");
+                               parseParms ($el, $mdef, 0);
+                               next;
+                       } else {
+                               $class_el = $doc->createElement('class');
+                               $class_el->setAttribute('name', StudlyCaps($classname));
+                               $class_el->setAttribute('cname', StudlyCaps($prefix));
+                               $ns_elem->appendChild($class_el);
+
+                               for ($j = 0; $j < $cnt; $j++) {
+                                       $mdef = delete $fdefs{$mnames[$i+$j]};
+                                       
+                                       $el = addNameElem($class_el, 'method', $mnames[$i+$j], $prefix);
+                                       if ($mdef =~ /^deprecated/) {
+                                               $el->setAttribute("deprecated", "1");
+                                               $mdef =~ s/deprecated//g;
+                                       }
+                                       $mdef =~ /(.*?)\w+\s*\(/;
+                                       addReturnElem($el, $1);
+                                       $el->setAttribute("shared", "true");
+                                       parseParms ($el, $mdef, 0);
+                               }
+                               $i += ($cnt - 1);
+                               next;
+                       }
+               }
+       }
+}
+
+sub addNameElem
+{
+       my ($node, $type, $cname, $prefix, $prepend) = @_;
+
+       my $elem = $doc->createElement($type);
+       $node->appendChild($elem);
+       if (defined $prefix) {
+               my $match;
+               if ($cname =~ /$prefix(\w+)/) {
+                       $match = $1;
+               } else {
+                       $match = $cname;
+               }
+               if ($prepend) {
+                       $name = $prepend . $match;
+               } else {
+                       $name = $match;
+               }
+               $elem->setAttribute('name', StudlyCaps($name));
+       }
+       if ($cname) {
+               $elem->setAttribute('cname', $cname);
+       }
+       return $elem;
+}
+
+sub addParamsElem
+{
+       my ($parent, @params) = @_;
+
+       my $parms_elem = $doc->createElement('parameters');
+       $parent->appendChild($parms_elem);
+       my $parm_num = 0;
+       foreach $parm (@params) {
+               $parm_num++;
+               $parm =~ s/\s+(\*+)/\1 /g;
+               my $out = $parm =~ s/G_CONST_RETURN/const/g;
+               $parm =~ s/(const\s+)?(\w+)\*\s+const\*/const \2\*/g;
+               $parm =~ s/(\*+)\s*const\s+/\1 /g;
+               $parm =~ s/(\w+)\s+const\s*\*/const \1\*/g;
+               $parm =~ s/const\s+/const-/g;
+               $parm =~ s/unsigned\s+/unsigned-/g;
+               if ($parm =~ /(.*)\(\s*\**\s*(\w+)\)\s+\((.*)\)/) {
+                       my $ret = $1; my $cbn = $2; my $params = $3;
+                       my $type = $parent->getAttribute('name') . StudlyCaps($cbn);
+                       $cb_elem = addNameElem($ns_elem, 'callback', $type, $ns);
+                       addReturnElem($cb_elem, $ret);
+                       if ($params && ($params ne "void")) {
+                               addParamsElem($cb_elem, split(/,/, $params));
+                               my $data_parm = $cb_elem->lastChild()->lastChild();
+                               if ($data_parm && $data_parm->getAttribute('type') eq "gpointer") {
+                                   $data_parm->setAttribute('name', 'data');
+                               }
+                       }
+                       $parm_elem = $doc->createElement('parameter');
+                       $parm_elem->setAttribute('type', $type);
+                       $parm_elem->setAttribute('name', $cbn);
+                       $parms_elem->appendChild($parm_elem);
+                       next;
+               } elsif ($parm =~ /\.\.\./) {
+                       $parm_elem = $doc->createElement('parameter');
+                       $parms_elem->appendChild($parm_elem);
+                       $parm_elem->setAttribute('ellipsis', 'true');
+                       next;
+               }
+               $parm_elem = $doc->createElement('parameter');
+               $parms_elem->appendChild($parm_elem);
+               my $name = "";
+               if ($parm =~ /struct\s+(\S+)\s+(\S+)/) {
+                       $parm_elem->setAttribute('type', $1);
+                       $name = $2;
+               }elsif ($parm =~ /(unsigned )?(\S+)\s+(\S+)/) {
+                       $parm_elem->setAttribute('type', $1 . $2);
+                       $name = $3;
+               } elsif ($parm =~ /(\w+\*)(\w+)/) {
+                       $parm_elem->setAttribute('type', $1);
+                       $name = $2;
+               } elsif ($parm =~ /(\S+)/) {
+                       $parm_elem->setAttribute('type', $1);
+                       $name = "arg" . $parm_num;
+               }
+               if ($name =~ /(\w+)\[.*\]/) {
+                       $name = $1;
+                       $parm_elem->setAttribute('array', "true");
+               }
+               if ($out) {
+                       $parm_elem->setAttribute('pass_as', "out");
+               }
+               $parm_elem->setAttribute('name', $name);
+       }
+}
+
+sub addReturnElem
+{
+       my ($parent, $ret) = @_;
+
+       $ret =~ s/(\w+)\s+const\s*\*/const \1\*/g;
+       $ret =~ s/const|G_CONST_RETURN/const-/g;
+       $ret =~ s/\s+//g;
+       $ret =~ s/(const-)?(\w+)\*(const-)\*/const-\2\*\*/g;
+       my $ret_elem = $doc->createElement('return-type');
+       $parent->appendChild($ret_elem);
+       $ret_elem->setAttribute('type', $ret);
+       if ($parent->getAttribute('name') eq "Copy" && $ret =~ /\*$/) {
+               $ret_elem->setAttribute('owned', 'true');
+       }
+       return $ret_elem;
+}
+
+sub addPropElem
+{
+       my ($spec, $node, $is_child) = @_;
+       my ($name, $mode, $docs);
+       $spec =~ /g_param_spec_(\w+)\s*\((.*)\s*\)\s*\)/;
+       my $type = $1;
+       my @params = split(/,/, $2);
+
+       $name = $params[0];
+       if ($defines{$name}) {
+               $name = $defines{$name};
+       } else {
+               $name =~ s/\s*\"//g;
+       }
+
+       $mode = $params[$#params];
+
+       if ($type =~ /boolean|float|double|^u?int|pointer|unichar/) {
+               $type = "g$type";
+       } elsif ($type =~ /string/) {
+               $type = "gchar*";
+       } elsif ($type =~ /boxed|object/) {
+               $type = $params[$#params-1];
+               $type =~ s/TYPE_//;
+               $type =~ s/\s+//g;
+               $type = StudlyCaps(lc($type));
+       } elsif ($type =~ /enum|flags/) {
+               $type = $params[$#params-2];
+               $type =~ s/TYPE_//;
+               $type =~ s/\s+//g;
+               $type = StudlyCaps(lc($type));
+       }
+
+       $prop_elem = $doc->createElement($is_child ? "childprop" : "property");
+       $node->appendChild($prop_elem);
+       $prop_elem->setAttribute('name', StudlyCaps($name));
+       $prop_elem->setAttribute('cname', $name);
+       $prop_elem->setAttribute('type', $type);
+
+       $prop_elem->setAttribute('readable', "true") if ($mode =~ /READ/);
+       $prop_elem->setAttribute('writeable', "true") if ($mode =~ /WRIT/);
+       $prop_elem->setAttribute('construct', "true") if ($mode =~ /CONSTRUCT(?!_)/);
+       $prop_elem->setAttribute('construct-only', "true") if ($mode =~ /CONSTRUCT_ONLY/);
+}
+
+sub parseTypeToken
+{
+       my ($tok) = @_;
+
+       if ($tok =~ /G_TYPE_(\w+)/) {
+               my $type = $1;
+               if ($type eq "NONE") {
+                       return "void";
+               } elsif ($type eq "INT") {
+                       return "gint32";
+               } elsif ($type eq "UINT") {
+                       return "guint32";
+               } elsif ($type eq "ENUM" || $type eq "FLAGS") {
+                       return "gint32";
+               } elsif ($type eq "STRING") {
+                       return "gchar*";
+               } elsif ($type eq "OBJECT") {
+                       return "GObject*";
+               } else {
+                       return "g" . lc ($type);
+               }
+       } else {
+               $tok =~ s/_TYPE//; 
+               $tok =~ s/\|.*STATIC_SCOPE//; 
+               $tok =~ s/\W+//g;
+               return StudlyCaps (lc($tok));
+       }
+}
+
+sub addSignalElem
+{
+       my ($spec, $class, $node) = @_;
+       $spec =~ s/\n\s*//g; $class =~ s/\n\s*//g;
+
+       $sig_elem = $doc->createElement('signal');
+       $node->appendChild($sig_elem);
+
+       if ($spec =~ /\(\"([\w\-]+)\"/) {
+               $sig_elem->setAttribute('name', StudlyCaps($1));
+               $sig_elem->setAttribute('cname', $1);
+       }
+       $sig_elem->setAttribute('when', $1) if ($spec =~ /_RUN_(\w+)/);
+
+       $sig_elem->setAttribute('manual', 'true') if ($spec =~ /G_TYPE_POINTER/);
+       if ($spec =~ /_OFFSET\s*\(\w+,\s*(\w+)\)/) {
+               my $method = $1;
+               $sig_elem->setAttribute('field_name', $method);
+
+               if ($class =~ /;\s*(\/\*< (public|protected|private) >\s*\*\/)?(G_CONST_RETURN\s+)?(\w+\s*\**)\s*\(\s*\*\s*$method\)\s*\((.*?)\);/) {
+                       $ret = $4; $parms = $5;
+                       addReturnElem($sig_elem, $ret);
+                       if ($parms && ($parms ne "void")) {
+                               my ($dump, @parm_arr) = split (/,/, $parms);
+                               addParamsElem($sig_elem, @parm_arr);
+                       }
+                       return $method;
+       } else {
+                       die "ERROR: Failed to parse method $method from class definition:\n$class";
+               }
+       } else {
+               @args = split(/,/, $spec);
+               my $rettype = parseTypeToken ($args[7]);
+               addReturnElem($sig_elem, $rettype);
+               $parmcnt = $args[8];
+               $parmcnt =~ s/.*(\d+).*/\1/;
+               $parms_elem = $doc->createElement('parameters');
+               $sig_elem->appendChild($parms_elem);
+
+               for (my $idx = 0; $idx < $parmcnt; $idx++) {
+                       my $argtype = parseTypeToken ($args[9+$idx]);
+                       $parm_elem = $doc->createElement('parameter');
+                       $parms_elem->appendChild($parm_elem);
+                       $parm_elem->setAttribute('name', "p$idx");
+                       $parm_elem->setAttribute('type', $argtype);
+               }
+               return "";
+       }
+               }
+
+sub addImplementsElem
+{
+       my ($spec, $node) = @_;
+       $spec =~ s/\n\s*//g; 
+       if ($spec =~ /,\s*(\w+)_TYPE_(\w+),/) {
+               $impl_elem = $doc->createElement('interface');
+               $name = StudlyCaps (lc ("$1_$2"));
+               $impl_elem->setAttribute ("cname", "$name");
+               $node->appendChild($impl_elem);
+       }
+}
+
+
+sub parseInitFunc
+{
+       my ($obj_el, $initfunc, $classdef) = @_;
+
+       my @init_lines = split (/\n/, $initfunc);
+       my @signal_vms = ();
+
+       my $linenum = 0;
+       while ($linenum < @init_lines) {
+
+               my $line = $init_lines[$linenum];
+                       
+               if ($line =~ /#define/) {
+                       # FIXME: This ignores the bool helper macro thingie.
+               } elsif ($line =~ /g_object_(class|interface)_install_prop/) {
+                       my $prop = $line;
+                       while ($prop !~ /\)\s*;/) {
+                               $prop .= $init_lines[++$linenum];
+                       }
+                       addPropElem ($prop, $obj_el, 0);
+                       $propcnt++;
+               } elsif ($line =~ /gtk_container_class_install_child_property/) {
+                       my $prop = $line;
+                       do {
+                               $prop .= $init_lines[++$linenum];
+                       } until ($init_lines[$linenum] =~ /\)\s*;/);
+                       addPropElem ($prop, $obj_el, 1);
+                       $childpropcnt++;
+               } elsif ($line =~ /\bg.*_signal_new/) {
+                       my $sig = $line;
+                       do {
+                               $sig .= $init_lines[++$linenum];
+                       } until ($init_lines[$linenum] =~ /;/);
+                       $signal_vm = addSignalElem ($sig, $classdef, $obj_el);
+                       push (@signal_vms, $signal_vm) if $signal_vm;
+                       $sigcnt++;
+               }
+               $linenum++;
+       }
+       return @signal_vms;
+}
+
+sub parseTypeFuncMacro
+{
+       my ($obj_el, $typefunc) = @_;
+
+       $impls_node = undef;
+       while ($typefunc =~ /G_IMPLEMENT_INTERFACE\s*\(\s*(\w+)/) {
+               $iface = $1;
+               if (not $impls_node) {
+                       $impls_node = $doc->createElement ("implements");
+                       $obj_el->appendChild ($impls_node);
+               }
+               addImplementsElem ($prop, $impl_node);
+               if ($iface =~ /(\w+)_TYPE_(\w+)/) {
+                       $impl_elem = $doc->createElement('interface');
+                       $name = StudlyCaps (lc ("$1_$2"));
+                       $impl_elem->setAttribute ("cname", "$name");
+                       $impls_node->appendChild($impl_elem);
+               }
+               $typefunc =~ s/G_IMPLEMENT_INTERFACE\s*\(.*?\)//;
+       }
+}
+
+sub parseTypeFunc
+{
+       my ($obj_el, $typefunc) = @_;
+
+       my @type_lines = split (/\n/, $typefunc);
+
+       my $linenum = 0;
+       $impl_node = undef;
+       while ($linenum < @type_lines) {
+
+               my $line = $type_lines[$linenum];
+                       
+               if ($line =~ /#define/) {
+                       # FIXME: This ignores the bool helper macro thingie.
+               } elsif ($line =~ /g_type_add_interface_static/) {
+                       my $prop = $line;
+                       do {
+                               $prop .= $type_lines[++$linenum];
+                       } until ($type_lines[$linenum] =~ /;/);
+                       if (not $impl_node) {
+                               $impl_node = $doc->createElement ("implements");
+                               $obj_el->appendChild ($impl_node);
+                       }
+                       addImplementsElem ($prop, $impl_node);
+               }
+               $linenum++;
+       }
+}
+
+##############################################################
+# Converts a dash or underscore separated name to StudlyCaps.
+##############################################################
+
+sub StudlyCaps
+{
+       my ($symb) = @_;
+       $symb =~ s/^([a-z])/\u\1/;
+       $symb =~ s/^(\d)/\1_/;
+       $symb =~ s/[-_]([a-z])/\u\1/g;
+       $symb =~ s/[-_](\d)/\1/g;
+       $symb =~ s/^2/Two/;
+       $symb =~ s/^3/Three/;
+       return $symb;
+}
+
diff --git a/parser/gapi_pp.pl b/parser/gapi_pp.pl
new file mode 100755 (executable)
index 0000000..6358605
--- /dev/null
@@ -0,0 +1,271 @@
+#!/usr/bin/perl
+#
+# gapi_pp.pl : A source preprocessor for the extraction of API info from a
+#             C library source directory.
+#
+# Authors: Mike Kestner <mkestner@speakeasy.net>
+#         Martin Willemoes Hansen <mwh@sysrq.dk>
+#
+# Copyright (c) 2001 Mike Kestner
+# Copyright (c) 2003 Martin Willemoes Hansen
+# Copyright (c) 2003-2004 Novell, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+$private_regex = '^#if.*(ENABLE_BACKEND|ENABLE_ENGINE)';
+$eatit_regex = '^#if(.*(__cplusplus|DEBUG|DISABLE_COMPAT|ENABLE_BROKEN)|\s+0\s*$)';
+$ignoreit_regex = '^\s+\*|#ident|#error|#\s*include|#\s*else|#\s*undef|G_(BEGIN|END)_DECLS|GDKVAR|GTKVAR|GTKMAIN_C_VAR|GTKTYPEUTILS_VAR|VARIABLE|GTKTYPEBUILTIN';
+
+foreach $arg (@ARGV) {
+       if (-d $arg && -e $arg) {
+               @hdrs = (@hdrs, `ls $arg/*.h`);
+               @srcs = (@srcs, `ls $arg/*.c`);
+       } elsif (-f $arg && -e $arg) {
+               @hdrs = (@hdrs, $arg) if ($arg =~ /\.h$/);
+               @srcs = (@srcs, $arg) if ($arg =~ /\.c$/);
+       } else {
+               die "unable to process arg: $arg";
+       }
+}
+
+foreach $fname (@hdrs) {
+
+       if ($fname =~ /test|private|internals|gtktextlayout|gtkmarshalers/) {
+               @privhdrs = (@privhdrs, $fname);
+               next;
+       }
+
+       open(INFILE, $fname) || die "Could open $fname\n";
+
+       $braces = 0;
+       $deprecated = -1;
+       $ifdeflevel = 0;
+       $prepend = "";
+       while ($line = <INFILE>) {
+               $braces++ if ($line =~ /{/ and $line !~ /}/);
+               $braces-- if ($line =~ /}/ and $line !~ /{/);
+               
+               next if ($line =~ /$ignoreit_regex/);
+
+               $line =~ s/\/\*[^<].*?\*\///g;
+               
+               next if ($line !~ /\S/);
+
+               $line = $prepend . $line;
+               $prepend = "";
+
+               while ($line =~ /(.*)\\$/) { $line = $1 . <INFILE>; }
+
+               if ($line =~ /#\s*define\s+\w+\s+\"/) {
+                       $def = $line;
+                       while ($def !~ /\".*\"/) {$def .= ($line = <INFILE>);}
+                       print $def;
+               } elsif ($line =~ /#\s*define\s+\w+\s*\D+/) {
+                       $def = $line;
+                       while ($line =~ /\\\n/) {$def .= ($line = <INFILE>);}
+                       if ($def =~ /_CHECK_\w*CAST|INSTANCE_GET_INTERFACE/) {
+                               $def =~ s/\\\n//g;
+                               print $def;
+                       }
+               } elsif ($line =~ /^\s*\/\*[^<]/) {
+                       while ($line !~ /\*\//) {$line = <INFILE>;}
+               } elsif ($line =~ /^extern/) {
+                       while ($line !~ /;/) {$line = <INFILE>;}
+               } elsif ($line =~ /^#ifndef\s+\w+_H_*\b/) {
+                       while ($line !~ /#define|#endif/) {$line = <INFILE>;}
+               } elsif ($line =~ /$private_regex/) {
+                       $nested = 0;
+                       while ($line = <INFILE>) {
+                               last if (!$nested && ($line =~ /#else|#endif/));
+                               if ($line =~ /#if/) {
+                                       $nested++;
+                               } elsif ($line =~ /#endif/) {
+                                       $nested--
+                               }
+                               next if ($line !~ /^struct/);
+
+                               print "private$line";
+                               do {
+                                       $line = <INFILE>;
+                                       print $line;
+                               } until ($line =~ /^\}/);
+                       }
+               } elsif ($line =~ /$eatit_regex/) {
+                       $nested = 0;
+                       while ($line = <INFILE>) {
+                               last if (!$nested && ($line =~ /#else|#endif/));
+                               if ($line =~ /#if/) {
+                                       $nested++;
+                               } elsif ($line =~ /#endif/) {
+                                       $nested--
+                               }
+                       }
+               } elsif ($line =~ /^#\s*ifn?\s*\!?def/) {
+                       $ifdeflevel++;
+                       #print "#ifn?def ($ifdeflevel): $line\n";
+                       if ($line =~ /#ifndef.*DISABLE_DEPRECATED/) {
+                               $deprecated = $ifdeflevel;
+                       } elsif ($line =~ /#if !defined.*DISABLE_DEPRECATED/) {
+                               $deprecated = $ifdeflevel;
+                       }
+               } elsif ($line =~ /^#\s*endif/) {
+                       #print "#endif   ($ifdeflevel): $line\n";
+                       if ($deprecated == $ifdeflevel) {
+                               $deprecated = -1;
+                       }
+                       $ifdeflevel--;
+               } elsif ($line =~ /typedef struct\s*\{?\s*$/) {
+                       while ($line !~ /{/) {
+                               chomp ($line);
+                               $line .= <INFILE>;
+                       }
+                       my $first_line = $line;
+                       my @lines = ();
+                       $line = <INFILE>;
+                       while ($line !~ /^}\s*(\w+);/) {
+                               if ($line =~ /\(.*\).*\(/) {
+                                       while ($line !~ /;/) {
+                                               chomp ($line);
+                                               $nxt = <INFILE>;
+                                               $nxt =~ s/^\s+/ /;
+                                               $line .= $nxt;
+                                       }
+                               }
+                               push @lines, $line;
+                               $line = <INFILE>;
+                       }
+                       $line =~ /^}\s*(\w+);/;
+                       my $name = $1;
+                       print "typedef struct _$name $name;\n";
+                       print "struct _$name {\n";
+                       foreach $line (@lines) {
+                               if ($line =~ /(\s*.+\;)/) {
+                                       $field = $1;
+                                       $field =~ s/(\w+) const/const $1/;
+                                       print "$field\n";
+                               }
+                       }
+                       print "};\n";
+               } elsif ($line =~ /^enum\s+\{/) {
+                       while ($line !~ /^};/) {$line = <INFILE>;}
+               } elsif ($line =~ /^(typedef\s+)?union/) {
+                       next if ($line =~ /^typedef\s+union\s+\w+\s+\w+;/);
+                       while ($line !~ /^};/) {$line = <INFILE>;}
+               } elsif ($line =~ /(\s+)union\s*{/) {
+                       # this is a hack for now, but I need it for the fields to work
+                       $indent = $1;
+                       $do_print = 1;
+                       while ($line !~ /^$indent}\s*\w+;/) {
+                               $line = <INFILE>;
+                               next if ($line !~ /;/);
+                               print $line if $do_print;
+                               $do_print = 0;
+                       }
+               } else {
+                       if ($braces or $line =~ /;|\/\*/) {
+                               if ($deprecated == -1) {
+                                       print $line;
+                               } else {
+                                       print "deprecated$line";
+                               }
+                       } else {
+                               $prepend = $line;
+                               $prepend =~ s/\n/ /g;
+                       }
+               }
+       }
+}
+
+foreach $fname (@srcs, @privhdrs) {
+
+       open(INFILE, $fname) || die "Could open $fname\n";
+
+       if ($fname =~ /builtins_ids/) {
+               while ($line = <INFILE>) {
+                       next if ($line !~ /\{/);
+
+                       chomp($line);
+                       $builtin = "BUILTIN" . $line;
+                       $builtin .= <INFILE>;
+                       print $builtin;
+               }
+               next;
+       }
+
+       while ($line = <INFILE>) {
+               next if ($line !~ /^(struct|\w+_class_init|\w+_base_init|\w+_get_type\b|G_DEFINE_TYPE_WITH_CODE)/);
+
+               if ($line =~ /^G_DEFINE_TYPE_WITH_CODE/) {
+                       my $macro;
+                       my $parens = 0;
+                       do {
+                               chomp ($line);
+                               $line =~ s/(.*)\\$/\1/;
+                               $line =~ s/^\s+(.*)/ \1/;
+                               $macro .= $line;
+                               foreach $chr (split (//, $line)) {
+                                       if ($chr eq "(") {
+                                               $parens++;
+                                       } elsif ($chr eq ")") {
+                                               $parens--;
+                                       }
+                               }
+                               $line = <INFILE>;
+                       } while ($parens > 0);
+                       print "$macro\n";
+                       next if ($line !~ /^(struct|\w+_class_init|\w+_base_init)/);
+               }
+
+               if ($line =~ /^struct/) {
+                       # need some of these to parse out parent types
+                       print "private";
+                       if ($line =~ /;/) {
+                               print $line;
+                               next;
+                       }
+               }
+
+               $comment = 0;
+               $begin = 0;
+               $end = 0;
+               do {
+                       # Following ifs strips out // and /* */ C comments
+                       if ($line =~ /\/\*/) {
+                               $comment = 1;
+                               $begin = 1;
+                       }
+
+                       if ($comment != 1) {
+                               $line =~ s/\/\/.*//;
+                               print $line;
+                       }
+
+                       if ($line =~ /\*\//) {
+                               $comment = 0;
+                               $end = 1;
+                       }
+
+                       if ($begin == 1 && $end == 1) {
+                               $line =~ s/\/\*.*\*\///;
+                               print $line;
+                       }
+
+                       $begin = 0;
+                       $end = 0;
+               } until (($line = <INFILE>) =~ /^}/);
+               print $line;
+       }
+}
+
index 48cc71e57c6580af2f1f4650dc439ec47e1c221c..4b6e66b299a75cd07891f072b186bde35ad02cf3 100644 (file)
@@ -1,7 +1,8 @@
 MAINTAINERCLEANFILES = Makefile.in
 
 api:
-       $(GAPI_PARSER) gstreamer-sharp-source.xml
+       $(MONO) $(top_builddir)/parser/gst-gapi-parser.exe \
+               gstreamer-sharp-source.xml
        grep -v "public const string " \
                $(top_srcdir)/gstreamer-sharp/Tag.custom \
                > $(top_srcdir)/gstreamer-sharp/Tag.custom.tmp