diff options
author | Rob Taylor <rob.taylor@codethink.co.uk> | 2008-02-08 15:31:03 +0000 |
---|---|---|
committer | Robert James Taylor <robtaylor@src.gnome.org> | 2008-02-08 15:31:03 +0000 |
commit | b935261f387dffa2c7006fe1be04820004810e87 (patch) | |
tree | 0c077cee05c725e1e56309374a1ceea8d8881b7d /tools | |
parent | c6c15af80d8a6d34e5c77d3111c638126edb3d45 (diff) | |
download | gobject-introspection-b935261f387dffa2c7006fe1be04820004810e87.tar.gz |
Added: Added: Renamed to tools/Makefile.am: Renamed to tools/compiler.c:
2008-02-08 Rob Taylor <rob.taylor@codethink.co.uk>
* Makefile.am:
* configure.ac:
* gidl/Makefile.am: Added:
* girepository/Makefile.am: Added:
* src/Makefile.am: Renamed to tools/Makefile.am:
* src/compiler.c: Renamed to tools/compiler.c:
* src/g-idl-offsets.pl: Renamed to tools/g-idl-offsets.pl:
* src/generate.c: Renamed to tools/generate.c:
* src/gidlmodule.c: Renamed to tools/gidlmodule.c:
* src/gidlmodule.h: Renamed to tools/gidlmodule.h:
* src/gidlnode.c: Renamed to tools/gidlnode.c:
* src/gidlnode.h: Renamed to tools/gidlnode.h:
* src/gidlparser.c: Renamed to tools/gidlparser.c:
* src/gidlparser.h: Renamed to tools/gidlparser.h:
* src/gidlwriter.c: Renamed to tools/gidlwriter.c:
* src/gidlwriter.h: Renamed to tools/gidlwriter.h:
* src/ginfo.c: Renamed to girepository/ginfo.c:
* src/ginvoke.c: Renamed to girepository/ginvoke.c:
* src/girepository.c: Renamed to girepository/girepository.c:
* src/girepository.h: Renamed to girepository/girepository.h:
* src/gmetadata.c: Renamed to girepository/gmetadata.c:
* src/gmetadata.h: Renamed to girepository/gmetadata.h:
* src/scanner.c: Renamed to tools/scanner.c:
* src/scanner.h: Renamed to tools/scanner.h:
* src/scannerlexer.l: Renamed to tools/scannerlexer.l:
* src/scannerparser.y: Renamed to tools/scannerparser.y:
* tests/invoke/Makefile.am:
Split src/ into girepository/ and tools/
* Makefile.am:
* configure.ac:
* girepository/Makefile.am:
* tests/Makefile.am:
* tests/invoke/Makefile.am:
* tests/parser/Makefile.am:
* tests/roundtrips.sh:
* tools/Makefile.am:
Make distcheck work.
svn path=/trunk/; revision=104
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile.am | 39 | ||||
-rw-r--r-- | tools/compiler.c | 257 | ||||
-rwxr-xr-x | tools/g-idl-offsets.pl | 258 | ||||
-rw-r--r-- | tools/generate.c | 1223 | ||||
-rw-r--r-- | tools/gidlmodule.c | 215 | ||||
-rw-r--r-- | tools/gidlmodule.h | 48 | ||||
-rw-r--r-- | tools/gidlnode.c | 2050 | ||||
-rw-r--r-- | tools/gidlnode.h | 334 | ||||
-rw-r--r-- | tools/gidlparser.c | 2054 | ||||
-rw-r--r-- | tools/gidlparser.h | 38 | ||||
-rw-r--r-- | tools/gidlwriter.c | 483 | ||||
-rw-r--r-- | tools/gidlwriter.h | 26 | ||||
-rw-r--r-- | tools/scanner.c | 1762 | ||||
-rw-r--r-- | tools/scanner.h | 167 | ||||
-rw-r--r-- | tools/scannerlexer.l | 311 | ||||
-rw-r--r-- | tools/scannerparser.y | 1376 |
16 files changed, 10641 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..eb2d144f --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,39 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = -DGIREPO_DEFAULT_SEARCH_PATH="\"$(libdir)\"" +BUILT_SOURCES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h +CLEANFILES = scannerparser.c scannerparser.h scannerlexer.c scannerlexer.h +AM_YFLAGS = -d + +# Why do I have to do this automake? +scannerlexer.h: scannerlexer.c + +noinst_LTLIBRARIES = libgirepository-parser.la +bin_PROGRAMS = g-idl-compiler g-idl-generate g-idl-scanner + +libgirepository_parser_la_SOURCES = \ + gidlmodule.c \ + gidlmodule.h \ + gidlnode.c \ + gidlnode.h \ + gidlparser.c \ + gidlparser.h +libgirepository_parser_la_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository + +g_idl_compiler_SOURCES = compiler.c +g_idl_compiler_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository +g_idl_compiler_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la libgirepository-parser.la + +g_idl_generate_SOURCES = generate.c +g_idl_generate_CFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository +g_idl_generate_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la + +g_idl_scanner_SOURCES = \ + scanner.c \ + scanner.h \ + scannerlexer.l \ + scannerparser.y \ + gidlwriter.c \ + gidlwriter.h +g_idl_scanner_CFLAGS = $(GIREPO_CFLAGS) $(SCANNER_CFLAGS) -I$(top_srcdir)/girepository +g_idl_scanner_LDADD = $(GIREPO_LIBS) $(SCANNER_LIBS) $(top_builddir)/girepository/libgirepository.la libgirepository-parser.la diff --git a/tools/compiler.c b/tools/compiler.c new file mode 100644 index 00000000..1c91632e --- /dev/null +++ b/tools/compiler.c @@ -0,0 +1,257 @@ +/* GObject introspection: Metadata compiler + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "gidlmodule.h" +#include "gidlnode.h" +#include "gidlparser.h" +#include "gmetadata.h" + +gboolean raw = FALSE; +gboolean no_init = FALSE; +gchar **input = NULL; +gchar *output = NULL; +gchar *mname = NULL; +gchar *shlib = NULL; +gboolean debug = FALSE; +gboolean verbose = FALSE; + +static gchar * +format_output (GMetadata *metadata) +{ + GString *result; + gint i; + + result = g_string_sized_new (6 * metadata->len); + + g_string_append_printf (result, "#include <stdlib.h>\n"); + g_string_append_printf (result, "#include <girepository.h>\n\n"); + + g_string_append_printf (result, "const unsigned char _G_METADATA[] = \n{"); + + for (i = 0; i < metadata->len; i++) + { + if (i > 0) + g_string_append (result, ", "); + + if (i % 10 == 0) + g_string_append (result, "\n\t"); + + g_string_append_printf (result, "0x%.2x", metadata->data[i]); + } + + g_string_append_printf (result, "\n};\n\n"); + g_string_append_printf (result, "const gsize _G_METADATA_SIZE = %u;\n\n", + (guint)metadata->len); + + if (!no_init) + { + g_string_append_printf (result, + "__attribute__((constructor)) void\n" + "register_metadata (void)\n" + "{\n" + "\tGMetadata *metadata;\n" + "\tmetadata = g_metadata_new_from_const_memory (_G_METADATA, _G_METADATA_SIZE);\n" + "\tg_irepository_register (NULL, metadata);\n" + "}\n\n"); + + g_string_append_printf (result, + "__attribute__((destructor)) void\n" + "unregister_metadata (void)\n" + "{\n" + "\tg_irepository_unregister (NULL, \"%s\");\n" + "}\n", + g_metadata_get_namespace (metadata)); + } + + return g_string_free (result, FALSE); +} + +static void +write_out_metadata (gchar *prefix, + GMetadata *metadata) +{ + FILE *file; + + if (output == NULL) + file = stdout; + else + { + gchar *filename; + + if (prefix) + filename = g_strdup_printf ("%s-%s", prefix, output); + else + filename = g_strdup (output); + file = g_fopen (filename, "w"); + + if (file == NULL) + { + g_fprintf (stderr, "failed to open '%s': %s\n", + filename, g_strerror (errno)); + g_free (filename); + + return; + } + + g_free (filename); + } + + if (raw) + fwrite (metadata->data, 1, metadata->len, file); + else + { + gchar *code; + + code = format_output (metadata); + fputs (code, file); + g_free (code); + } + + if (output != NULL) + fclose (file); +} + +GLogLevelFlags logged_levels; + +static void log_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + + if (log_level & logged_levels) + g_log_default_handler (log_domain, log_level, message, user_data); +} + +static GOptionEntry options[] = +{ + { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "emit raw metadata", NULL }, + { "code", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &raw, "emit C code", NULL }, + { "no-init", 0, 0, G_OPTION_ARG_NONE, &no_init, "do not create _init() function", NULL }, + { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" }, + { "module", 'm', 0, G_OPTION_ARG_STRING, &mname, "module to compile", "NAME" }, + { "shared-library", 'l', 0, G_OPTION_ARG_FILENAME, &shlib, "shared library", "FILE" }, + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "show debug messages", NULL }, + { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, "show verbose messages", NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &input, NULL, NULL }, + { NULL, } +}; + +int +main (int argc, char ** argv) +{ + GOptionContext *context; + GError *error = NULL; + GList *m, *modules; + gint i; + + g_metadata_check_sanity (); + + context = g_option_context_new (""); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, &error); + g_option_context_free (context); + + logged_levels = G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_DEBUG); + if (debug) + logged_levels = logged_levels | G_LOG_LEVEL_DEBUG; + if (verbose) + logged_levels = logged_levels | G_LOG_LEVEL_MESSAGE; + + g_log_set_default_handler (log_handler, NULL); + + if (!input) + { + g_fprintf (stderr, "no input files\n"); + + return 1; + } + + modules = NULL; + for (i = 0; input[i]; i++) + { + GList *mods; + mods = g_idl_parse_file (input[i], &error); + + if (mods == NULL) + { + g_fprintf (stderr, "error parsing file %s: %s\n", + input[i], error->message); + + return 1; + } + + modules = g_list_concat (modules, mods); + } + + for (m = modules; m; m = m->next) + { + GIdlModule *module = m->data; + gchar *prefix; + GMetadata *metadata; + + if (mname && strcmp (mname, module->name) != 0) + continue; + if (shlib) + { + if (module->shared_library) + g_free (module->shared_library); + module->shared_library = g_strdup (shlib); + } + metadata = g_idl_module_build_metadata (module, modules); + if (metadata == NULL) + { + g_error ("Failed to build metadata for module '%s'\n", module->name); + + continue; + } + if (!g_metadata_validate (metadata, &error)) + g_error ("Invalid metadata for module '%s': %s", + module->name, error->message); + + + if (!mname && (m->next || m->prev) && output) + prefix = module->name; + else + prefix = NULL; + + write_out_metadata (prefix, metadata); + g_metadata_free (metadata); + metadata = NULL; + + /* when writing to stdout, stop after the first module */ + if (m->next && !output && !mname) + { + g_warning ("%d modules omitted\n", g_list_length (modules) - 1); + + break; + } + } + + return 0; +} diff --git a/tools/g-idl-offsets.pl b/tools/g-idl-offsets.pl new file mode 100755 index 00000000..55318708 --- /dev/null +++ b/tools/g-idl-offsets.pl @@ -0,0 +1,258 @@ +#! /usr/bin/perl -w + +use Getopt::Long; +use XML::Parser; + +my $INCLUDES; +my $PRINT_VERSION; +my $PRINT_HELP; + +%optctl = (includes => \$INCLUDES); + +GetOptions(\%optctl, "includes=s"); + +my $file = shift; + +die "Can't find file \"$file\"" + unless -f $file; + +my $parser = new XML::Parser(ErrorContext => 2); + +my $structname = ""; +my $classname = ""; + +open OUTPUT, ">dump-sizes.c" || die "Cannot open dump-sizes.c: $!\n"; + +print OUTPUT <<EOT; +#include <glib.h> +$INCLUDES + +int +main (int argc, char *argv[]) +{ +EOT + +$parser->setHandlers(Start => \&start_handler, + End => \&end_handler); + +$parser->parsefile($file); + +print OUTPUT <<EOT; + + return 0; +} +EOT + +close OUTPUT; + +$CC = $ENV{CC} ? $ENV{CC} : "gcc"; +$LD = $ENV{LD} ? $ENV{LD} : $CC; +$CFLAGS = $ENV{CFLAGS} ? $ENV{CFLAGS} : ""; +$LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : ""; + +my $o_file; +if ($CC =~ /libtool/) { + $o_file = "dump-sizes.lo" +} else { + $o_file = "dump-sizes.o" +} + +$command = "$CC $CFLAGS -c -o $o_file dump-sizes.c && $LD -o dump-sizes $o_file $LDFLAGS"; + +system($command) == 0 or die "Compilation failed\n"; + +system("./dump-sizes > sizes.out") == 0 or die "Scan failed\n"; + +#unlink "./dump-sizes.c", "./dump-sizes.o", "./dump-sizes.lo", "./dump-sizes"; + +my %Sizes; + +open SIZES, "<sizes.out" || die "Can't open sizes.out: $!"; +while (<SIZES>) { + if (m/(\w*) (\w*) (\d*)/) { + $Sizes{$1 . "." . $2} = $3; + } +} + +close SIZES; + +open OUTPUT2, ">$file.new" || die "Cannot open $file.new: $!\n"; + +$parser->setHandlers(Start => \&dump_start_handler, + End => \&dump_end_handler); + +$parser->parsefile($file); + +close OUTPUT2; + +sub start_handler +{ + my $p = shift; + my $el = shift; + my $name = ""; + my $cname = ""; + my $class = ""; + + while (@_) { + my $att = shift; + my $val = shift; + + if ($att eq "name") { + $name = $val; + } + elsif ($att eq "cname") { + $cname = $val; + } + elsif ($att eq "class") { + $class = $val; + } + } + + if (($el eq "object") || ($el eq "interface")) { + if (!($cname eq "")) { + $structname = $cname; + } + else { + $structname = $name; + } + + if (!($class eq "")) { + $classname = $class; + } + elsif ($el eq "object") { + $classname = $structname . "Class"; + } + else { + $classname = $structname . "Iface"; + } + } + elsif ($el eq "field") { + if (!($cname eq "")) { + need_offset ($structname, $cname); + } + else { + need_offset ($structname, $name); + } + } + elsif ($el eq "vfunc") { + if (!($cname eq "")) { + need_offset ($classname, $cname); + } + else { + need_offset ($classname, $name); + } + } +} + +sub end_handler +{ + my $p = shift; + my $el = shift; + + if (($el eq "object") || + ($el eq "interface") || + ($el eq "boxed") || + ($el eq "struct") || + ($el eq "union")) { + $structname = ""; + $classname = ""; + } +} + +sub need_offset +{ + my $struct = shift; + my $name = shift; + + print OUTPUT " g_print (\"$struct $name %d\\n\", G_STRUCT_OFFSET ($struct, $name));\n"; +} + +sub write_offset +{ + my $struct = shift; + my $name = shift; + + print OUTPUT2 " offset=\"" . $Sizes{$struct . "." . $name } . "\"" +} + +sub dump_start_handler +{ + my $p = shift; + my $el = shift; + my $name = ""; + my $cname = ""; + my $class = ""; + + print OUTPUT2 "<$el"; + + while (@_) { + my $att = shift; + my $val = shift; + + print OUTPUT2 " $att=\"$val\""; + + if ($att eq "name") { + $name = $val; + } + elsif ($att eq "cname") { + $cname = $val; + } + elsif ($att eq "class") { + $class = $val; + } + } + + if (($el eq "object") || ($el eq "interface")) { + if (!($cname eq "")) { + $structname = $cname; + } + else { + $structname = $name; + } + + if (!($class eq "")) { + $classname = $class; + } + elsif ($el eq "object") { + $classname = $structname . "Class"; + } + else { + $classname = $structname . "Iface"; + } + } + elsif ($el eq "field") { + if (!($cname eq "")) { + write_offset ($structname, $cname); + } + else { + write_offset ($structname, $name); + } + } + elsif ($el eq "vfunc") { + if (!($cname eq "")) { + write_offset ($classname, $cname); + } + else { + write_offset ($classname, $name); + } + } + print OUTPUT2 ">\n"; +} + +sub dump_end_handler +{ + my $p = shift; + my $el = shift; + + if (($el eq "object") || + ($el eq "interface") || + ($el eq "boxed") || + ($el eq "struct") || + ($el eq "union")) { + $structname = ""; + $classname = ""; + } + + print OUTPUT2 "</$el>\n"; +} + diff --git a/tools/generate.c b/tools/generate.c new file mode 100644 index 00000000..8ae6fc35 --- /dev/null +++ b/tools/generate.c @@ -0,0 +1,1223 @@ +/* -*- Mode: C; c-file-style: "gnu"; -*- */ +/* GObject introspection: IDL generator + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <errno.h> +#include <dlfcn.h> +#include <string.h> + +#include <glib.h> +#include <glib-object.h> +#include <glib/gstdio.h> + +#include "girepository.h" +#include "gmetadata.h" + +gboolean raw = FALSE; +gchar **input = NULL; +gchar *output = NULL; + +static void +write_type_name (const gchar *namespace, + GIBaseInfo *info, + FILE *file) +{ + if (strcmp (namespace, g_base_info_get_namespace (info)) != 0) + g_fprintf (file, "%s.", g_base_info_get_namespace (info)); + + g_fprintf (file, "%s", g_base_info_get_name (info)); +} + +static void +write_type_info (const gchar *namespace, + GITypeInfo *info, + FILE *file) +{ + gint tag; + gint i; + GITypeInfo *type; + + const gchar* basic[] = { + "void", + "gboolean", + "gint8", + "guint8", + "gint16", + "guint16", + "gint32", + "guint32", + "gint64", + "guint64", + "gint", + "guint", + "glong", + "gulong", + "gssize", + "gsize", + "gfloat", + "gdouble", + "utf8", + "filename" + }; + + tag = g_type_info_get_tag (info); + + if (tag < 18) + g_fprintf (file, "%s%s", basic[tag], g_type_info_is_pointer (info) ? "*" : ""); + else if (tag < 20) + g_fprintf (file, "%s", basic[tag]); + else if (tag == 20) + { + gint length; + + type = g_type_info_get_param_type (info, 0); + write_type_info (namespace, type, file); + g_fprintf (file, "["); + + length = g_type_info_get_array_length (info); + + if (length >= 0) + g_fprintf (file, "length=%d", length); + + if (g_type_info_is_zero_terminated (info)) + g_fprintf (file, "%szero-terminated=1", length >= 0 ? "," : ""); + + g_fprintf (file, "]"); + g_base_info_unref ((GIBaseInfo *)type); + } + else if (tag == 21) + { + GIBaseInfo *iface = g_type_info_get_interface (info); + write_type_name (namespace, iface, file); + if (g_type_info_is_pointer (info)) + g_fprintf (file, "*"); + g_base_info_unref (iface); + } + else if (tag == 22) + { + type = g_type_info_get_param_type (info, 0); + g_fprintf (file, "GList"); + if (type) + { + g_fprintf (file, "<"); + write_type_info (namespace, type, file); + g_fprintf (file, ">"); + g_base_info_unref ((GIBaseInfo *)type); + } + g_fprintf (file, "*"); + } + else if (tag == 23) + { + type = g_type_info_get_param_type (info, 0); + g_fprintf (file, "GSList"); + if (type) + { + g_fprintf (file, "<"); + write_type_info (namespace, type, file); + g_fprintf (file, ">"); + g_base_info_unref ((GIBaseInfo *)type); + } + g_fprintf (file, "*"); + } + else if (tag == 24) + { + type = g_type_info_get_param_type (info, 0); + g_fprintf (file, "GHashTable"); + if (type) + { + g_fprintf (file, "<"); + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + type = g_type_info_get_param_type (info, 1); + g_fprintf (file, ","); + write_type_info (namespace, type, file); + g_fprintf (file, ">"); + g_base_info_unref ((GIBaseInfo *)type); + } + g_fprintf (file, "*"); + } + else if (tag == 25) + { + gint n; + + g_fprintf (file, "GError"); + n = g_type_info_get_n_error_domains (info); + if (n > 0) + { + g_fprintf (file, "<"); + for (i = 0; i < n; i++) + { + GIErrorDomainInfo *ed = g_type_info_get_error_domain (info, i); + if (i > 0) + g_fprintf (file, ","); + write_type_name (namespace, (GIBaseInfo *)ed, file); + g_base_info_unref ((GIBaseInfo *)ed); + } + g_fprintf (file, ">"); + } + g_fprintf (file, "*"); + } +} + +static void +write_constant_value (const gchar *namespace, + GITypeInfo *info, + GArgument *argument, + FILE *file); + +static void +write_field_info (const gchar *namespace, + GIFieldInfo *info, + GIConstantInfo *branch, + FILE *file) +{ + const gchar *name; + GIFieldInfoFlags flags; + gint size; + gint offset; + GITypeInfo *type; + GArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_field_info_get_flags (info); + size = g_field_info_get_size (info); + offset = g_field_info_get_offset (info); + + g_fprintf (file, + " <field name=\"%s\" readable=\"%s\" writable=\"%s\" ", + name, + flags & GI_FIELD_IS_READABLE ? "1" : "0", + flags & GI_FIELD_IS_WRITABLE ? "1" : "0"); + if (size) + g_fprintf (file, "bits=\"%d\" ", size); + g_fprintf (file, "offset=\"%d\" ", offset); + + g_fprintf (file, "type=\""); + + type = g_field_info_get_type (info); + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + + g_fprintf (file, "\""); + + if (branch) + { + g_fprintf (file, " branch=\""); + type = g_constant_info_get_type (branch); + g_constant_info_get_value (branch, &value); + write_constant_value (namespace, type, &value, file); + g_fprintf (file, "\""); + } + + g_fprintf (file," />\n"); +} + +static void +write_callable_info (const gchar *namespace, + GICallableInfo *info, + FILE *file, + gint indent) +{ + GITypeInfo *type; + gint i; + + g_fprintf (file, "%*s <return-type type=\"", indent, ""); + + type = g_callable_info_get_return_type (info); + write_type_info (namespace, type, file); + + g_fprintf (file, "\""); + + if (g_type_info_is_pointer (type)) + { + switch (g_callable_info_get_caller_owns (info)) + { + case GI_TRANSFER_NOTHING: + g_fprintf (file, " transfer=\"none\""); + break; + case GI_TRANSFER_CONTAINER: + g_fprintf (file, " transfer=\"shallow\""); + break; + case GI_TRANSFER_EVERYTHING: + g_fprintf (file, " transfer=\"full\""); + break; + default: + g_assert_not_reached (); + } + } + g_base_info_unref ((GIBaseInfo *)type); + if (g_callable_info_may_return_null (info)) + g_fprintf (file, " null-ok=\"1\""); + + g_fprintf (file, " />\n"); + + if (g_callable_info_get_n_args (info) > 0) + { + g_fprintf (file, "%*s <parameters>\n", indent, ""); + for (i = 0; i < g_callable_info_get_n_args (info); i++) + { + GIArgInfo *arg = g_callable_info_get_arg (info, i); + + g_fprintf (file, "%*s <parameter name=\"%s\" type=\"", + indent, "", g_base_info_get_name ((GIBaseInfo *) arg)); + + type = g_arg_info_get_type (arg); + write_type_info (namespace, type, file); + g_fprintf (file, "\""); + + if (g_type_info_is_pointer (type)) + { + switch (g_arg_info_get_ownership_transfer (arg)) + { + case GI_TRANSFER_NOTHING: + g_fprintf (file, " transfer=\"none\""); + break; + case GI_TRANSFER_CONTAINER: + g_fprintf (file, " transfer=\"shallow\""); + break; + case GI_TRANSFER_EVERYTHING: + g_fprintf (file, " transfer=\"full\""); + break; + default: + g_assert_not_reached (); + } + } + g_base_info_unref ((GIBaseInfo *)type); + + g_fprintf (file, " direction=\""); + switch (g_arg_info_get_direction (arg)) + { + case GI_DIRECTION_IN: + g_fprintf (file, "in"); + break; + case GI_DIRECTION_OUT: + g_fprintf (file, "out"); + break; + case GI_DIRECTION_INOUT: + g_fprintf (file, "inout"); + break; + } + g_fprintf (file, "\""); + + if (g_arg_info_may_be_null (arg)) + g_fprintf (file, " null-ok=\"1\""); + + if (g_arg_info_is_dipper (arg)) + g_fprintf (file, " dipper=\"1\""); + + if (g_arg_info_is_return_value (arg)) + g_fprintf (file, " retval=\"1\""); + + if (g_arg_info_is_optional (arg)) + g_fprintf (file, " optional=\"1\""); + + g_fprintf (file, " />\n"); + + g_base_info_unref ((GIBaseInfo *)arg); + } + + g_fprintf (file, "%*s </parameters>\n", indent, ""); + } +} + +static void +write_function_info (const gchar *namespace, + GIFunctionInfo *info, + FILE *file, + gint indent) +{ + GIFunctionInfoFlags flags; + const gchar *tag; + const gchar *name; + const gchar *symbol; + gboolean deprecated; + + flags = g_function_info_get_flags (info); + name = g_base_info_get_name ((GIBaseInfo *)info); + symbol = g_function_info_get_symbol (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) + tag = "constructor"; + else if (flags & GI_FUNCTION_IS_METHOD) + tag = "method"; + else + tag = "function"; + + g_fprintf (file, "%*s<%s name=\"%s\" symbol=\"%s\"", + indent, "", tag, name, symbol); + + if (flags & GI_FUNCTION_IS_SETTER) + g_fprintf (file, " type=\"setter\""); + else if (flags & GI_FUNCTION_IS_GETTER) + g_fprintf (file, " type=\"getter\""); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + write_callable_info (namespace, (GICallableInfo*)info, file, indent); + g_fprintf (file, "%*s</%s>\n", indent, "", tag); +} + +static void +write_callback_info (const gchar *namespace, + GICallbackInfo *info, + FILE *file, + gint indent) +{ + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + g_fprintf (file, "%*s<callback name=\"%s\"", indent, "", name); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + write_callable_info (namespace, (GICallableInfo*)info, file, indent); + g_fprintf (file, "%*s</callback>\n", indent, ""); +} + +static void +write_struct_info (const gchar *namespace, + GIStructInfo *info, + FILE *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_BOXED) + { + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + g_fprintf (file, " <boxed name=\"%s\" type-name=\"%s\" get-type=\"%s\"", name, type_name, type_init); + } + else + g_fprintf (file, " <struct name=\"%s\"", name); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + + for (i = 0; i < g_struct_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_struct_info_get_field (info, i); + write_field_info (namespace, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_struct_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_struct_info_get_method (info, i); + write_function_info (namespace, function, file, 6); + g_base_info_unref ((GIBaseInfo *)function); + } + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_BOXED) + g_fprintf (file, " </boxed>\n"); + else + g_fprintf (file, " </struct>\n"); +} + +static void +write_value_info (const gchar *namespace, + GIValueInfo *info, + FILE *file) +{ + const gchar *name; + glong value; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + value = g_value_info_get_value (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + g_fprintf (file, " <member name=\"%s\" value=\"%ld\"", name, value); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, " />\n"); +} + +static void +write_constant_value (const gchar *namespace, + GITypeInfo *type, + GArgument *value, + FILE *file) +{ + switch (g_type_info_get_tag (type)) + { + case GI_TYPE_TAG_BOOLEAN: + g_fprintf (file, "%d", value->v_boolean); + break; + case GI_TYPE_TAG_INT8: + g_fprintf (file, "%d", value->v_int8); + break; + case GI_TYPE_TAG_UINT8: + g_fprintf (file, "%d", value->v_uint8); + break; + case GI_TYPE_TAG_INT16: + g_fprintf (file, "%" G_GINT16_FORMAT, value->v_int16); + break; + case GI_TYPE_TAG_UINT16: + g_fprintf (file, "%" G_GUINT16_FORMAT, value->v_uint16); + break; + case GI_TYPE_TAG_INT32: + g_fprintf (file, "%" G_GINT32_FORMAT, value->v_int32); + break; + case GI_TYPE_TAG_UINT32: + g_fprintf (file, "%" G_GUINT32_FORMAT, value->v_uint32); + break; + case GI_TYPE_TAG_INT64: + g_fprintf (file, "%" G_GINT64_FORMAT, value->v_int64); + break; + case GI_TYPE_TAG_UINT64: + g_fprintf (file, "%" G_GUINT64_FORMAT, value->v_uint64); + break; + case GI_TYPE_TAG_INT: + g_fprintf (file, "%d", value->v_int); + break; + case GI_TYPE_TAG_UINT: + g_fprintf (file, "%d", value->v_uint); + break; + case GI_TYPE_TAG_LONG: + g_fprintf (file, "%ld", value->v_long); + break; + case GI_TYPE_TAG_ULONG: + g_fprintf (file, "%ld", value->v_ulong); + break; + case GI_TYPE_TAG_SSIZE: + g_fprintf (file, "%zd", value->v_ssize); + break; + case GI_TYPE_TAG_SIZE: + g_fprintf (file, "%zd", value->v_size); + break; + case GI_TYPE_TAG_FLOAT: + g_fprintf (file, "%f", value->v_float); + break; + case GI_TYPE_TAG_DOUBLE: + g_fprintf (file, "%f", value->v_double); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + g_fprintf (file, "%s", value->v_string); + break; + default: + g_assert_not_reached (); + } +} + +static void +write_constant_info (const gchar *namespace, + GIConstantInfo *info, + FILE *file, + gint indent) +{ + GITypeInfo *type; + const gchar *name; + gboolean deprecated; + GArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + g_fprintf (file, "%*s<constant name=\"%s\" type=\"", indent, "", name); + + type = g_constant_info_get_type (info); + write_type_info (namespace, type, file); + g_fprintf (file, "\" value=\""); + + g_constant_info_get_value (info, &value); + write_constant_value (namespace, type, &value, file); + g_fprintf (file, "\" />\n"); + + g_base_info_unref ((GIBaseInfo *)type); +} + + +static void +write_enum_info (const gchar *namespace, + GIEnumInfo *info, + FILE *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_ENUM) + g_fprintf (file, " <enum "); + else + g_fprintf (file, " <flags "); + g_fprintf (file, "name=\"%s\"", name); + + if (type_init) + g_fprintf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + + for (i = 0; i < g_enum_info_get_n_values (info); i++) + { + GIValueInfo *value = g_enum_info_get_value (info, i); + write_value_info (namespace, value, file); + g_base_info_unref ((GIBaseInfo *)value); + } + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_ENUM) + g_fprintf (file, " </enum>\n"); + else + g_fprintf (file, " </flags>\n"); +} + +static void +write_signal_info (const gchar *namespace, + GISignalInfo *info, + FILE *file) +{ + GSignalFlags flags; + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_signal_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + g_fprintf (file, " <signal name=\"%s\"", name); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + if (flags & G_SIGNAL_RUN_FIRST) + g_fprintf (file, " when=\"FIRST\""); + else if (flags & G_SIGNAL_RUN_LAST) + g_fprintf (file, " when=\"LAST\""); + else if (flags & G_SIGNAL_RUN_CLEANUP) + g_fprintf (file, " when=\"CLEANUP\""); + + if (flags & G_SIGNAL_NO_RECURSE) + g_fprintf (file, " no-recurse=\"1\""); + + if (flags & G_SIGNAL_DETAILED) + g_fprintf (file, " detailed=\"1\""); + + if (flags & G_SIGNAL_ACTION) + g_fprintf (file, " action=\"1\""); + + if (flags & G_SIGNAL_NO_HOOKS) + g_fprintf (file, " no-hooks=\"1\""); + + g_fprintf (file, ">\n"); + + write_callable_info (namespace, (GICallableInfo*)info, file, 6); + g_fprintf (file, " </signal>\n"); +} + +static void +write_vfunc_info (const gchar *namespace, + GIVFuncInfo *info, + FILE *file) +{ + GIVFuncInfoFlags flags; + const gchar *name; + gboolean deprecated; + gint offset; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_vfunc_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + offset = g_vfunc_info_get_offset (info); + + g_fprintf (file, " <vfunc name=\"%s\"", name); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + if (flags & GI_VFUNC_MUST_CHAIN_UP) + g_fprintf (file, " must-chain-up=\"1\""); + + if (flags & GI_VFUNC_MUST_OVERRIDE) + g_fprintf (file, " override=\"always\""); + else if (flags & GI_VFUNC_MUST_NOT_OVERRIDE) + g_fprintf (file, " override=\"never\""); + + g_fprintf (file, " offset=\"%d\"", offset); + g_fprintf (file, ">\n"); + + write_callable_info (namespace, (GICallableInfo*)info, file, 6); + g_fprintf (file, " </vfunc>\n"); +} + +static void +write_property_info (const gchar *namespace, + GIPropertyInfo *info, + FILE *file) +{ + GParamFlags flags; + const gchar *name; + gboolean deprecated; + GITypeInfo *type; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_property_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + g_fprintf (file, " <property name=\"%s\"", name); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + if (flags & G_PARAM_READABLE) + g_fprintf (file, " readable=\"1\""); + else + g_fprintf (file, " readable=\"0\""); + + if (flags & G_PARAM_WRITABLE) + g_fprintf (file, " writable=\"1\""); + else + g_fprintf (file, " writable=\"0\""); + + if (flags & G_PARAM_CONSTRUCT) + g_fprintf (file, " construct=\"1\""); + + if (flags & G_PARAM_CONSTRUCT_ONLY) + g_fprintf (file, " construct-only=\"1\""); + + type = g_property_info_get_type (info); + g_fprintf (file, " type=\""); + write_type_info (namespace, type, file); + g_fprintf (file, "\""); + + g_fprintf (file, " />\n"); +} + +static void +write_object_info (const gchar *namespace, + GIObjectInfo *info, + FILE *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + gboolean deprecated; + GIObjectInfo *pnode; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + g_fprintf (file, " <object name=\"%s\"", name); + + pnode = g_object_info_get_parent (info); + if (pnode) + { + g_fprintf (file, " parent=\""); + write_type_name (namespace, (GIBaseInfo *)pnode, file); + g_fprintf (file, "\"" ); + g_base_info_unref ((GIBaseInfo *)pnode); + } + + g_fprintf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + + if (g_object_info_get_n_interfaces (info) > 0) + { + g_fprintf (file, " <implements>\n"); + for (i = 0; i < g_object_info_get_n_interfaces (info); i++) + { + GIInterfaceInfo *imp = g_object_info_get_interface (info, i); + g_fprintf (file, " <interface name=\""); + write_type_name (namespace, (GIBaseInfo*)imp, file); + g_fprintf (file,"\" />\n"); + g_base_info_unref ((GIBaseInfo*)imp); + } + g_fprintf (file, " </implements>\n"); + } + + for (i = 0; i < g_object_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_object_info_get_field (info, i); + write_field_info (namespace, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_object_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_object_info_get_method (info, i); + write_function_info (namespace, function, file, 6); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_object_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_object_info_get_property (info, i); + write_property_info (namespace, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_object_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_object_info_get_signal (info, i); + write_signal_info (namespace, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_object_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_object_info_get_vfunc (info, i); + write_vfunc_info (namespace, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_object_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_object_info_get_constant (info, i); + write_constant_info (namespace, constant, file, 6); + g_base_info_unref ((GIBaseInfo *)constant); + } + + g_fprintf (file, " </object>\n"); +} + +static void +write_interface_info (const gchar *namespace, + GIInterfaceInfo *info, + FILE *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + g_fprintf (file, " <interface name=\"%s\" type-name=\"%s\" get-type=\"%s\"", + name, type_name, type_init); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + + if (g_interface_info_get_n_prerequisites (info) > 0) + { + g_fprintf (file, " <requires>\n"); + for (i = 0; i < g_interface_info_get_n_prerequisites (info); i++) + { + GIBaseInfo *req = g_interface_info_get_prerequisite (info, i); + + if (g_base_info_get_type (req) == GI_INFO_TYPE_INTERFACE) + g_fprintf (file, " <interface name=\""); + else + g_fprintf (file, " <object name=\""); + write_type_name (namespace, req, file); + g_fprintf (file, "\" />\n"); + g_base_info_unref (req); + } + g_fprintf (file, " </requires>\n"); + } + + for (i = 0; i < g_interface_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_interface_info_get_method (info, i); + write_function_info (namespace, function, file, 6); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_interface_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_interface_info_get_property (info, i); + write_property_info (namespace, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_interface_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_interface_info_get_signal (info, i); + write_signal_info (namespace, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_interface_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_interface_info_get_vfunc (info, i); + write_vfunc_info (namespace, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_interface_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_interface_info_get_constant (info, i); + write_constant_info (namespace, constant, file, 6); + g_base_info_unref ((GIBaseInfo *)constant); + } + + g_fprintf (file, " </interface>\n"); +} + +static void +write_error_domain_info (const gchar *namespace, + GIErrorDomainInfo *info, + FILE *file) +{ + GIBaseInfo *enum_; + const gchar *name, *quark; + + name = g_base_info_get_name ((GIBaseInfo *)info); + quark = g_error_domain_info_get_quark (info); + enum_ = (GIBaseInfo *)g_error_domain_info_get_codes (info); + g_fprintf (file, + " <errordomain name=\"%s\" get-quark=\"%s\" codes=\"", + name, quark); + write_type_name (namespace, enum_, file); + g_fprintf (file, "\" />\n"); + g_base_info_unref (enum_); +} + +static void +write_union_info (const gchar *namespace, + GIUnionInfo *info, + FILE *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + g_fprintf (file, " <union name=\"%s\"", name); + + if (type_name) + g_fprintf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init); + + if (deprecated) + g_fprintf (file, " deprecated=\"1\""); + + g_fprintf (file, ">\n"); + + if (g_union_info_is_discriminated (info)) + { + gint offset; + GITypeInfo *type; + + offset = g_union_info_get_discriminator_offset (info); + type = g_union_info_get_discriminator_type (info); + + g_fprintf (file, " <discriminator offset=\"%d\" type=\"", offset); + write_type_info (namespace, type, file); + g_fprintf (file, "\" />\n"); + g_base_info_unref ((GIBaseInfo *)type); + } + + for (i = 0; i < g_union_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_union_info_get_field (info, i); + GIConstantInfo *constant = g_union_info_get_discriminator (info, i); + write_field_info (namespace, field, constant, file); + g_base_info_unref ((GIBaseInfo *)field); + if (constant) + g_base_info_unref ((GIBaseInfo *)constant); + } + + for (i = 0; i < g_union_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_union_info_get_method (info, i); + write_function_info (namespace, function, file, 6); + g_base_info_unref ((GIBaseInfo *)function); + } + + g_fprintf (file, " </union>\n"); +} + +static void +write_repository (GIRepository *repository, + gboolean needs_prefix) +{ + FILE *file; + gchar **namespaces; + gchar *ns; + gint i, j; + + namespaces = g_irepository_get_namespaces (repository); + + if (output == NULL) + file = stdout; + else + { + gchar *filename; + + if (needs_prefix) + filename = g_strdup_printf ("%s-%s", namespaces[0], output); + else + filename = g_strdup (output); + file = g_fopen (filename, "w"); + + if (file == NULL) + { + g_fprintf (stderr, "failed to open '%s': %s\n", + filename, g_strerror (errno)); + g_free (filename); + + return; + } + + g_free (filename); + } + + g_fprintf (file, "<?xml version=\"1.0\"?>\n"); + g_fprintf (file, "<api version=\"1.0\">\n"); + + for (i = 0; namespaces[i]; i++) + { + const gchar *shared_library; + ns = namespaces[i]; + shared_library = g_irepository_get_shared_library (repository, ns); + if (shared_library) + g_fprintf (file, " <namespace name=\"%s\" shared-library=\"%s\">\n", + ns, shared_library); + else + g_fprintf (file, " <namespace name=\"%s\">\n", ns); + + for (j = 0; j < g_irepository_get_n_infos (repository, ns); j++) + { + GIBaseInfo *info = g_irepository_get_info (repository, ns, j); + switch (g_base_info_get_type (info)) + { + case GI_INFO_TYPE_FUNCTION: + write_function_info (ns, (GIFunctionInfo *)info, file, 4); + break; + + case GI_INFO_TYPE_CALLBACK: + write_callback_info (ns, (GICallbackInfo *)info, file, 4); + break; + + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + write_struct_info (ns, (GIStructInfo *)info, file); + break; + + case GI_INFO_TYPE_UNION: + write_union_info (ns, (GIUnionInfo *)info, file); + break; + + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + write_enum_info (ns, (GIEnumInfo *)info, file); + break; + + case GI_INFO_TYPE_CONSTANT: + write_constant_info (ns, (GIConstantInfo *)info, file, 4); + break; + + case GI_INFO_TYPE_OBJECT: + write_object_info (ns, (GIObjectInfo *)info, file); + break; + + case GI_INFO_TYPE_INTERFACE: + write_interface_info (ns, (GIInterfaceInfo *)info, file); + break; + + case GI_INFO_TYPE_ERROR_DOMAIN: + write_error_domain_info (ns, (GIErrorDomainInfo *)info, file); + break; + + default: + g_error ("unknown info type %d\n", g_base_info_get_type (info)); + } + + g_base_info_unref (info); + } + + g_fprintf (file, " </namespace>\n"); + } + + g_fprintf (file, "</api>\n"); + + if (output != NULL) + fclose (file); + + g_strfreev (namespaces); +} + +static GOptionEntry options[] = +{ + { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "handle raw metadata", NULL }, + { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &input, NULL, NULL }, + { NULL, } +}; + +static const guchar * +load_metadata (const gchar *filename, + GModule **dlhandle, + gsize *len) +{ + guchar *metadata; + gsize *metadata_size; + GModule *handle; + + handle = g_module_open (filename, G_MODULE_BIND_LOCAL|G_MODULE_BIND_LAZY); + if (!g_module_symbol (handle, "_G_METADATA", (gpointer *) &metadata)) + { + g_printerr ("Could not load metadata from '%s': %s\n", + filename, g_module_error ()); + return NULL; + } + + if (!g_module_symbol (handle, "_G_METADATA_SIZE", (gpointer *) &metadata_size)) + { + g_printerr ("Could not load metadata from '%s': %s\n", + filename, g_module_error ()); + return NULL; + } + + *len = *metadata_size; + + if (dlhandle) + *dlhandle = handle; + + return metadata; +} + +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + gboolean needs_prefix; + gint i; + GMetadata *data; + + g_type_init (); + + g_metadata_check_sanity (); + + context = g_option_context_new (""); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, &error); + + if (!input) + { + g_fprintf (stderr, "no input files\n"); + + return 1; + } + + for (i = 0; input[i]; i++) + { + GModule *dlhandle = NULL; + const guchar *metadata; + gsize len; + + if (raw) + { + if (!g_file_get_contents (input[i], (gchar **)&metadata, &len, &error)) + { + g_fprintf (stderr, "failed to read '%s': %s\n", + input[i], error->message); + g_clear_error (&error); + continue; + } + } + else + { + metadata = load_metadata (input[i], &dlhandle, &len); + if (!metadata) + { + g_fprintf (stderr, "failed to load metadata from '%s'\n", + input[i]); + continue; + } + } + + if (input[i + 1] && output) + needs_prefix = TRUE; + else + needs_prefix = FALSE; + + data = g_metadata_new_from_const_memory (metadata, len); + { + GError *error = NULL; + if (!g_metadata_validate (data, &error)) { + g_printerr ("metadata not valid: %s\n", error->message); + g_clear_error (&error); + } + } + g_irepository_register (g_irepository_get_default (), data); + write_repository (g_irepository_get_default (), needs_prefix); + g_irepository_unregister (g_irepository_get_default (), + g_metadata_get_namespace (data)); + + if (dlhandle) + { + g_module_close (dlhandle); + dlhandle = NULL; + } + + /* when writing to stdout, stop after the first module */ + if (input[i + 1] && !output) + { + g_fprintf (stderr, "warning, %d modules omitted\n", + g_strv_length (input) - 1); + + break; + } + } + + return 0; +} diff --git a/tools/gidlmodule.c b/tools/gidlmodule.c new file mode 100644 index 00000000..d17a249a --- /dev/null +++ b/tools/gidlmodule.c @@ -0,0 +1,215 @@ +/* GObject introspection: Metadata creation + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <string.h> + +#include "gidlmodule.h" +#include "gidlnode.h" + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + + +GIdlModule * +g_idl_module_new (const gchar *name, const gchar *shared_library) +{ + GIdlModule *module; + + module = g_new (GIdlModule, 1); + + module->name = g_strdup (name); + if (shared_library) + module->shared_library = g_strdup (shared_library); + else + module->shared_library = NULL; + module->entries = NULL; + + return module; +} + +void +g_idl_module_free (GIdlModule *module) +{ + GList *e; + + g_free (module->name); + + for (e = module->entries; e; e = e->next) + g_idl_node_free ((GIdlNode *)e->data); + + g_list_free (module->entries); + + g_free (module); +} + +GMetadata * +g_idl_module_build_metadata (GIdlModule *module, + GList *modules) +{ + guchar *metadata; + gsize length; + gint i; + GList *e; + Header *header; + DirEntry *entry; + guint32 header_size; + guint32 dir_size; + guint32 n_entries; + guint32 n_local_entries; + guint32 size, offset, offset2, old_offset; + GHashTable *strings; + GHashTable *types; + guchar *data; + + header_size = ALIGN_VALUE (sizeof (Header), 4); + n_local_entries = g_list_length (module->entries); + + restart: + init_stats (); + strings = g_hash_table_new (g_str_hash, g_str_equal); + types = g_hash_table_new (g_str_hash, g_str_equal); + n_entries = g_list_length (module->entries); + + g_message ("%d entries (%d local)\n", n_entries, n_local_entries); + + dir_size = n_entries * 12; + size = header_size + dir_size; + + size += ALIGN_VALUE (strlen (module->name) + 1, 4); + + for (e = module->entries; e; e = e->next) + { + GIdlNode *node = e->data; + + size += g_idl_node_get_full_size (node); + } + + g_message ("allocating %d bytes (%d header, %d directory, %d entries)\n", + size, header_size, dir_size, size - header_size - dir_size); + + data = g_malloc0 (size); + + /* fill in header */ + header = (Header *)data; + memcpy (header, G_IDL_MAGIC, 16); + header->major_version = 1; + header->minor_version = 0; + header->reserved = 0; + header->n_entries = n_entries; + header->n_local_entries = n_local_entries; + header->n_annotations = 0; + header->annotations = 0; /* filled in later */ + header->size = 0; /* filled in later */ + header->namespace = write_string (module->name, strings, data, &header_size); + header->shared_library = (module->shared_library? + write_string (module->shared_library, strings, data, &header_size) + : 0); + header->directory = ALIGN_VALUE (header_size, 4); + header->entry_blob_size = 12; + header->function_blob_size = 16; + header->callback_blob_size = 12; + header->signal_blob_size = 12; + header->vfunc_blob_size = 16; + header->arg_blob_size = 12; + header->property_blob_size = 12; + header->field_blob_size = 12; + header->value_blob_size = 12; + header->constant_blob_size = 20; + header->error_domain_blob_size = 16; + header->annotation_blob_size = 12; + header->signature_blob_size = 8; + header->enum_blob_size = 20; + header->struct_blob_size = 20; + header->object_blob_size = 32; + header->interface_blob_size = 28; + header->union_blob_size = 28; + + /* fill in directory and content */ + entry = (DirEntry *)&data[header->directory]; + + offset2 = header->directory + dir_size; + + for (e = module->entries, i = 0; e; e = e->next, i++) + { + GIdlNode *node = e->data; + + if (strchr (node->name, '.')) + { + g_error ("Names may not contain '.'"); + } + + /* we picked up implicit xref nodes, start over */ + if (i == n_entries) + { + g_message ("Found implicit cross references, starting over"); + + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + strings = NULL; + + g_free (data); + data = NULL; + + goto restart; + } + + offset = offset2; + + if (node->type == G_IDL_NODE_XREF) + { + entry->blob_type = 0; + entry->local = FALSE; + entry->offset = write_string (((GIdlNodeXRef*)node)->namespace, strings, data, &offset2); + entry->name = write_string (node->name, strings, data, &offset2); + } + else + { + old_offset = offset; + offset2 = offset + g_idl_node_get_size (node); + + entry->blob_type = node->type; + entry->local = TRUE; + entry->offset = offset; + entry->name = write_string (node->name, strings, data, &offset2); + + g_idl_node_build_metadata (node, module, modules, + strings, types, data, &offset, &offset2); + + if (offset2 > old_offset + g_idl_node_get_full_size (node)) + g_error ("left a hole of %d bytes\n", offset2 - old_offset - g_idl_node_get_full_size (node)); + } + + entry++; + } + + dump_stats (); + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + + header->annotations = offset2; + + g_message ("reallocating to %d bytes", offset2); + + metadata = g_realloc (data, offset2); + length = header->size = offset2; + return g_metadata_new_from_memory (metadata, length); +} + diff --git a/tools/gidlmodule.h b/tools/gidlmodule.h new file mode 100644 index 00000000..3564a75f --- /dev/null +++ b/tools/gidlmodule.h @@ -0,0 +1,48 @@ +/* GObject introspection: Parsed IDL + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IDL_MODULE_H__ +#define __G_IDL_MODULE_H__ + +#include <glib.h> +#include "gmetadata.h" + +G_BEGIN_DECLS + + +typedef struct _GIdlModule GIdlModule; + +struct _GIdlModule +{ + gchar *name; + gchar *shared_library; + GList *entries; +}; + +GIdlModule *g_idl_module_new (const gchar *name, + const gchar *module_filename); +void g_idl_module_free (GIdlModule *module); + +GMetadata * g_idl_module_build_metadata (GIdlModule *module, + GList *modules); + +G_END_DECLS + +#endif /* __G_IDL_MODULE_H__ */ diff --git a/tools/gidlnode.c b/tools/gidlnode.c new file mode 100644 index 00000000..f70cffb5 --- /dev/null +++ b/tools/gidlnode.c @@ -0,0 +1,2050 @@ +/* GObject introspection: Metadata creation + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gidlmodule.h" +#include "gidlnode.h" +#include "gmetadata.h" + +static gulong string_count = 0; +static gulong unique_string_count = 0; +static gulong string_size = 0; +static gulong unique_string_size = 0; +static gulong types_count = 0; +static gulong unique_types_count = 0; + +void +init_stats (void) +{ + string_count = 0; + unique_string_count = 0; + string_size = 0; + unique_string_size = 0; + types_count = 0; + unique_types_count = 0; +} + +void +dump_stats (void) +{ + g_message ("%lu strings (%lu before sharing), %lu bytes (%lu before sharing)", + unique_string_count, string_count, unique_string_size, string_size); + g_message ("%lu types (%lu before sharing)", unique_types_count, types_count); +} + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + + +GIdlNode * +g_idl_node_new (GIdlNodeTypeId type) +{ + GIdlNode *node = NULL; + + switch (type) + { + case G_IDL_NODE_FUNCTION: + case G_IDL_NODE_CALLBACK: + node = g_malloc0 (sizeof (GIdlNodeFunction)); + break; + + case G_IDL_NODE_PARAM: + node = g_malloc0 (sizeof (GIdlNodeParam)); + break; + + case G_IDL_NODE_TYPE: + node = g_malloc0 (sizeof (GIdlNodeType)); + break; + + case G_IDL_NODE_OBJECT: + case G_IDL_NODE_INTERFACE: + node = g_malloc0 (sizeof (GIdlNodeInterface)); + break; + + case G_IDL_NODE_SIGNAL: + node = g_malloc0 (sizeof (GIdlNodeSignal)); + break; + + case G_IDL_NODE_PROPERTY: + node = g_malloc0 (sizeof (GIdlNodeProperty)); + break; + + case G_IDL_NODE_VFUNC: + node = g_malloc0 (sizeof (GIdlNodeFunction)); + break; + + case G_IDL_NODE_FIELD: + node = g_malloc0 (sizeof (GIdlNodeField)); + break; + + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + node = g_malloc0 (sizeof (GIdlNodeEnum)); + break; + + case G_IDL_NODE_BOXED: + node = g_malloc0 (sizeof (GIdlNodeBoxed)); + break; + + case G_IDL_NODE_STRUCT: + node = g_malloc0 (sizeof (GIdlNodeStruct)); + break; + + case G_IDL_NODE_VALUE: + node = g_malloc0 (sizeof (GIdlNodeValue)); + break; + + case G_IDL_NODE_CONSTANT: + node = g_malloc0 (sizeof (GIdlNodeConstant)); + break; + + case G_IDL_NODE_ERROR_DOMAIN: + node = g_malloc0 (sizeof (GIdlNodeErrorDomain)); + break; + + case G_IDL_NODE_XREF: + node = g_malloc0 (sizeof (GIdlNodeXRef)); + break; + + case G_IDL_NODE_UNION: + node = g_malloc0 (sizeof (GIdlNodeUnion)); + break; + + default: + g_error ("Unhandled node type %d\n", type); + break; + } + + node->type = type; + + return node; +} + +void +g_idl_node_free (GIdlNode *node) +{ + GList *l; + + if (node == NULL) + return; + + switch (node->type) + { + case G_IDL_NODE_FUNCTION: + case G_IDL_NODE_CALLBACK: + { + GIdlNodeFunction *function = (GIdlNodeFunction *)node; + + g_free (node->name); + g_free (function->symbol); + g_idl_node_free ((GIdlNode *)function->result); + for (l = function->parameters; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (function->parameters); + } + break; + + case G_IDL_NODE_TYPE: + { + GIdlNodeType *type = (GIdlNodeType *)node; + + g_free (node->name); + g_idl_node_free ((GIdlNode *)type->parameter_type1); + g_idl_node_free ((GIdlNode *)type->parameter_type2); + + g_free (type->interface); + g_strfreev (type->errors); + + } + break; + + case G_IDL_NODE_PARAM: + { + GIdlNodeParam *param = (GIdlNodeParam *)node; + + g_free (node->name); + g_idl_node_free ((GIdlNode *)param->type); + } + break; + + case G_IDL_NODE_PROPERTY: + { + GIdlNodeProperty *property = (GIdlNodeProperty *)node; + + g_free (node->name); + g_idl_node_free ((GIdlNode *)property->type); + } + break; + + case G_IDL_NODE_SIGNAL: + { + GIdlNodeSignal *signal = (GIdlNodeSignal *)node; + + g_free (node->name); + for (l = signal->parameters; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (signal->parameters); + g_idl_node_free ((GIdlNode *)signal->result); + } + break; + + case G_IDL_NODE_VFUNC: + { + GIdlNodeVFunc *vfunc = (GIdlNodeVFunc *)node; + + g_free (node->name); + for (l = vfunc->parameters; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (vfunc->parameters); + g_idl_node_free ((GIdlNode *)vfunc->result); + } + break; + + case G_IDL_NODE_FIELD: + { + GIdlNodeField *field = (GIdlNodeField *)node; + + g_free (node->name); + g_idl_node_free ((GIdlNode *)field->type); + } + break; + + case G_IDL_NODE_OBJECT: + case G_IDL_NODE_INTERFACE: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + g_free (node->name); + g_free (iface->gtype_name); + g_free (iface->gtype_init); + + g_free (iface->parent); + + for (l = iface->interfaces; l; l = l->next) + g_free ((GIdlNode *)l->data); + g_list_free (iface->interfaces); + + for (l = iface->members; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (iface->members); + + } + break; + + case G_IDL_NODE_VALUE: + { + g_free (node->name); + } + break; + + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + { + GIdlNodeEnum *enum_ = (GIdlNodeEnum *)node; + + g_free (node->name); + g_free (enum_->gtype_name); + g_free (enum_->gtype_init); + + for (l = enum_->values; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (enum_->values); + } + break; + + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed = (GIdlNodeBoxed *)node; + + g_free (node->name); + g_free (boxed->gtype_name); + g_free (boxed->gtype_init); + + for (l = boxed->members; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (boxed->members); + } + break; + + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_ = (GIdlNodeStruct *)node; + + g_free (node->name); + for (l = struct_->members; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + g_list_free (struct_->members); + } + break; + + case G_IDL_NODE_CONSTANT: + { + GIdlNodeConstant *constant = (GIdlNodeConstant *)node; + + g_free (node->name); + g_free (constant->value); + g_idl_node_free ((GIdlNode *)constant->type); + } + break; + + case G_IDL_NODE_ERROR_DOMAIN: + { + GIdlNodeErrorDomain *domain = (GIdlNodeErrorDomain *)node; + + g_free (node->name); + g_free (domain->getquark); + g_free (domain->codes); + } + break; + + case G_IDL_NODE_XREF: + { + GIdlNodeXRef *xref = (GIdlNodeXRef *)node; + + g_free (node->name); + g_free (xref->namespace); + } + break; + + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_ = (GIdlNodeUnion *)node; + + g_free (node->name); + g_free (union_->gtype_name); + g_free (union_->gtype_init); + + g_idl_node_free ((GIdlNode *)union_->discriminator_type); + for (l = union_->members; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + g_idl_node_free ((GIdlNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type %d\n", node->type); + break; + } + + g_free (node); +} + +/* returns the fixed size of the blob */ +guint32 +g_idl_node_get_size (GIdlNode *node) +{ + GList *l; + gint size, n; + + switch (node->type) + { + case G_IDL_NODE_CALLBACK: + size = 12; + break; + + case G_IDL_NODE_FUNCTION: + size = 16; + break; + + case G_IDL_NODE_PARAM: + size = 12; + break; + + case G_IDL_NODE_TYPE: + size = 4; + break; + + case G_IDL_NODE_OBJECT: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = 32 + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_INTERFACE: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = 28 + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + { + GIdlNodeEnum *enum_ = (GIdlNodeEnum *)node; + + size = 20; + for (l = enum_->values; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_VALUE: + size = 12; + break; + + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_ = (GIdlNodeStruct *)node; + + size = 20; + for (l = struct_->members; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed = (GIdlNodeBoxed *)node; + + size = 20; + for (l = boxed->members; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_PROPERTY: + size = 12; + break; + + case G_IDL_NODE_SIGNAL: + size = 12; + break; + + case G_IDL_NODE_VFUNC: + size = 16; + break; + + case G_IDL_NODE_FIELD: + size = 12; + break; + + case G_IDL_NODE_CONSTANT: + size = 20; + break; + + case G_IDL_NODE_ERROR_DOMAIN: + size = 16; + break; + + case G_IDL_NODE_XREF: + size = 0; + break; + + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_ = (GIdlNodeUnion *)node; + + size = 28; + for (l = union_->members; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += g_idl_node_get_size ((GIdlNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type %d\n", node->type); + size = 0; + } + + g_debug ("node %p type %d size %d", node, node->type, size); + + return size; +} + +/* returns the full size of the blob including variable-size parts */ +guint32 +g_idl_node_get_full_size (GIdlNode *node) +{ + GList *l; + gint size, n; + + g_assert (node != NULL); + + switch (node->type) + { + case G_IDL_NODE_CALLBACK: + { + GIdlNodeFunction *function = (GIdlNodeFunction *)node; + size = 12; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = function->parameters; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + size += g_idl_node_get_full_size ((GIdlNode *)function->result); + } + break; + + case G_IDL_NODE_FUNCTION: + { + GIdlNodeFunction *function = (GIdlNodeFunction *)node; + size = 24; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (function->symbol) + 1, 4); + for (l = function->parameters; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + size += g_idl_node_get_full_size ((GIdlNode *)function->result); + } + break; + + case G_IDL_NODE_PARAM: + { + GIdlNodeParam *param = (GIdlNodeParam *)node; + + size = 12; + if (node->name) + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += g_idl_node_get_full_size ((GIdlNode *)param->type); + } + break; + + case G_IDL_NODE_TYPE: + { + GIdlNodeType *type = (GIdlNodeType *)node; + if (type->tag < TYPE_TAG_ARRAY) + size = 4; + else + { + switch (type->tag) + { + case TYPE_TAG_ARRAY: + size = 4 + 4; + if (type->parameter_type1) + size += g_idl_node_get_full_size ((GIdlNode *)type->parameter_type1); + break; + case TYPE_TAG_INTERFACE: + size = 4 + 4; + break; + case TYPE_TAG_LIST: + case TYPE_TAG_SLIST: + size = 4 + 4; + if (type->parameter_type1) + size += g_idl_node_get_full_size ((GIdlNode *)type->parameter_type1); + break; + case TYPE_TAG_HASH: + size = 4 + 4 + 4; + if (type->parameter_type1) + size += g_idl_node_get_full_size ((GIdlNode *)type->parameter_type1); + if (type->parameter_type2) + size += g_idl_node_get_full_size ((GIdlNode *)type->parameter_type2); + break; + case TYPE_TAG_ERROR: + { + gint n; + + if (type->errors) + n = g_strv_length (type->errors); + else + n = 0; + + size = 4 + 4 + 2 * (n + n % 2); + } + break; + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + break; + + case G_IDL_NODE_OBJECT: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = 32; + if (iface->parent) + size += ALIGN_VALUE (strlen (iface->parent) + 1, 4); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_INTERFACE: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = 28; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + { + GIdlNodeEnum *enum_ = (GIdlNodeEnum *)node; + + size = 20; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (enum_->gtype_name) + { + size += ALIGN_VALUE (strlen (enum_->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (enum_->gtype_init) + 1, 4); + } + + for (l = enum_->values; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_VALUE: + { + size = 12; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + } + break; + + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_ = (GIdlNodeStruct *)node; + + size = 20; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = struct_->members; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed = (GIdlNodeBoxed *)node; + + size = 20; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (boxed->gtype_name) + { + size += ALIGN_VALUE (strlen (boxed->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (boxed->gtype_init) + 1, 4); + } + for (l = boxed->members; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + case G_IDL_NODE_PROPERTY: + { + GIdlNodeProperty *prop = (GIdlNodeProperty *)node; + + size = 12; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += g_idl_node_get_full_size ((GIdlNode *)prop->type); + } + break; + + case G_IDL_NODE_SIGNAL: + { + GIdlNodeSignal *signal = (GIdlNodeSignal *)node; + + size = 12; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = signal->parameters; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + size += g_idl_node_get_full_size ((GIdlNode *)signal->result); + } + break; + + case G_IDL_NODE_VFUNC: + { + GIdlNodeVFunc *vfunc = (GIdlNodeVFunc *)node; + + size = 16; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = vfunc->parameters; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + size += g_idl_node_get_full_size ((GIdlNode *)vfunc->result); + } + break; + + case G_IDL_NODE_FIELD: + { + GIdlNodeField *field = (GIdlNodeField *)node; + + size = 12; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += g_idl_node_get_full_size ((GIdlNode *)field->type); + } + break; + + case G_IDL_NODE_CONSTANT: + { + GIdlNodeConstant *constant = (GIdlNodeConstant *)node; + + size = 20; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + /* FIXME non-string values */ + size += ALIGN_VALUE (strlen (constant->value) + 1, 4); + size += g_idl_node_get_full_size ((GIdlNode *)constant->type); + } + break; + + case G_IDL_NODE_ERROR_DOMAIN: + { + GIdlNodeErrorDomain *domain = (GIdlNodeErrorDomain *)node; + + size = 16; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (domain->getquark) + 1, 4); + } + break; + + case G_IDL_NODE_XREF: + { + GIdlNodeXRef *xref = (GIdlNodeXRef *)node; + + size = 0; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (xref->namespace) + 1, 4); + } + break; + + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_ = (GIdlNodeUnion *)node; + + size = 28; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = union_->members; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += g_idl_node_get_full_size ((GIdlNode *)l->data); + } + break; + + default: + g_error ("Unknown type tag %d\n", node->type); + size = 0; + } + + g_debug ("node %p type %d full size %d", node, node->type, size); + + return size; +} + +int +g_idl_node_cmp (GIdlNode *node, + GIdlNode *other) +{ + if (node->type < other->type) + return -1; + else if (node->type > other->type) + return 1; + else + return strcmp (node->name, other->name); +} + +void +g_idl_node_add_member (GIdlNode *node, + GIdlNodeFunction *member) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (member != NULL); + + switch (node->type) + { + case G_IDL_NODE_OBJECT: + case G_IDL_NODE_INTERFACE: + { + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + iface->members = + g_list_insert_sorted (iface->members, member, + (GCompareFunc) g_idl_node_cmp); + break; + } + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed = (GIdlNodeBoxed *)node; + boxed->members = + g_list_insert_sorted (boxed->members, member, + (GCompareFunc) g_idl_node_cmp); + break; + } + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_ = (GIdlNodeStruct *)node; + struct_->members = + g_list_insert_sorted (struct_->members, member, + (GCompareFunc) g_idl_node_cmp); + break; + } + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_ = (GIdlNodeUnion *)node; + union_->members = + g_list_insert_sorted (union_->members, member, + (GCompareFunc) g_idl_node_cmp); + break; + } + default: + g_error ("Cannot add a member to unknown type tag type %d\n", + node->type); + break; + } +} + +static gint64 +parse_int_value (const gchar *str) +{ + return strtoll (str, NULL, 0); +} + +static guint64 +parse_uint_value (const gchar *str) +{ + return strtoull (str, NULL, 0); +} + +static gdouble +parse_float_value (const gchar *str) +{ + return strtod (str, NULL); +} + +static gboolean +parse_boolean_value (const gchar *str) +{ + if (strcmp (str, "TRUE") == 0) + return TRUE; + + if (strcmp (str, "FALSE") == 0) + return FALSE; + + return parse_int_value (str) ? TRUE : FALSE; +} + +static GIdlNode * +find_entry_node (GIdlModule *module, + GList *modules, + const gchar *name, + guint16 *idx) + +{ + GList *l; + gint i; + gchar **names; + gint n_names; + GIdlNode *result = NULL; + + names = g_strsplit (name, ".", 0); + n_names = g_strv_length (names); + if (n_names > 2) + g_error ("Too many name parts"); + + for (l = module->entries, i = 1; l; l = l->next, i++) + { + GIdlNode *node = (GIdlNode *)l->data; + + if (n_names > 1) + { + if (node->type != G_IDL_NODE_XREF) + continue; + + if (((GIdlNodeXRef *)node)->namespace == NULL || + strcmp (((GIdlNodeXRef *)node)->namespace, names[0]) != 0) + continue; + } + + if (strcmp (node->name, names[n_names - 1]) == 0) + { + if (idx) + *idx = i; + + result = node; + goto out; + } + } + + if (n_names > 1) + { + GIdlNode *node = g_idl_node_new (G_IDL_NODE_XREF); + + ((GIdlNodeXRef *)node)->namespace = g_strdup (names[0]); + node->name = g_strdup (names[1]); + + module->entries = g_list_append (module->entries, node); + + if (idx) + *idx = g_list_length (module->entries); + + result = node; + + goto out; + } + + g_warning ("Entry %s not found", name); + + out: + + g_strfreev (names); + + return result; +} + +static guint16 +find_entry (GIdlModule *module, + GList *modules, + const gchar *name) +{ + guint16 idx = 0; + + find_entry_node (module, modules, name, &idx); + + return idx; +} + +static void +serialize_type (GIdlModule *module, + GList *modules, + GIdlNodeType *node, + GString *str) +{ + gint i; + const gchar* basic[] = { + "void", + "gboolean", + "gint8", + "guint8", + "gint16", + "guint16", + "gint32", + "guint32", + "gint64", + "guint64", + "gint", + "guint", + "glong", + "gulong", + "gssize", + "gsize", + "gfloat", + "gdouble", + "utf8", + "filename" + }; + + if (node->tag < 20) + { + g_string_append_printf (str, "%s%s", + basic[node->tag], node->is_pointer ? "*" : ""); + } + else if (node->tag == 20) + { + serialize_type (module, modules, node->parameter_type1, str); + g_string_append (str, "["); + + if (node->has_length) + g_string_append_printf (str, "length=%d", node->length); + + if (node->zero_terminated) + g_string_append_printf (str, "%szero-terminated=1", + node->has_length ? "," : ""); + + g_string_append (str, "]"); + } + else if (node->tag == 21) + { + GIdlNode *iface; + gchar *name; + + iface = find_entry_node (module, modules, node->interface, NULL); + if (iface) + name = iface->name; + else + { + g_warning ("Interface for type reference %s not found", node->interface); + name = node->interface; + } + + g_string_append_printf (str, "%s%s", name, node->is_pointer ? "*" : ""); + } + else if (node->tag == 22) + { + g_string_append (str, "GList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (module, modules, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == 23) + { + g_string_append (str, "GSList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (module, modules, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == 24) + { + g_string_append (str, "GHashTable<"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (module, modules, node->parameter_type1, str); + g_string_append (str, ","); + serialize_type (module, modules, node->parameter_type2, str); + g_string_append (str, ">"); + } + } + else if (node->tag == 25) + { + g_string_append (str, "GError"); + if (node->errors) + { + g_string_append (str, "<"); + for (i = 0; node->errors[i]; i++) + { + if (i > 0) + g_string_append (str, ","); + g_string_append (str, node->errors[i]); + } + g_string_append (str, ">"); + } + } +} + +void +g_idl_node_build_metadata (GIdlNode *node, + GIdlModule *module, + GList *modules, + GHashTable *strings, + GHashTable *types, + guchar *data, + guint32 *offset, + guint32 *offset2) +{ + GList *l; + guint32 old_offset = *offset; + guint32 old_offset2 = *offset2; + + switch (node->type) + { + case G_IDL_NODE_TYPE: + { + GIdlNodeType *type = (GIdlNodeType *)node; + SimpleTypeBlob *blob = (SimpleTypeBlob *)&data[*offset]; + + *offset += 4; + + if (type->tag < TYPE_TAG_ARRAY) + { + blob->reserved = 0; + blob->reserved2 = 0; + blob->pointer = type->is_pointer; + blob->reserved3 = 0; + blob->tag = type->tag; + } + else + { + GString *str; + gchar *s; + gpointer value; + + str = g_string_new (0); + serialize_type (module, modules, type, str); + s = g_string_free (str, FALSE); + + types_count += 1; + value = g_hash_table_lookup (types, s); + if (value) + { + blob->offset = GPOINTER_TO_INT (value); + g_free (s); + } + else + { + unique_types_count += 1; + g_hash_table_insert (types, s, GINT_TO_POINTER(*offset2)); + + blob->offset = *offset2; + switch (type->tag) + { + case TYPE_TAG_ARRAY: + { + ArrayTypeBlob *array = (ArrayTypeBlob *)&data[*offset2]; + guint32 pos; + + array->pointer = 1; + array->reserved = 0; + array->tag = type->tag; + array->zero_terminated = type->zero_terminated; + array->has_length = type->has_length; + array->reserved2 = 0; + array->length = type->length; + + pos = *offset2 + 4; + *offset2 += 8; + + g_idl_node_build_metadata ((GIdlNode *)type->parameter_type1, + module, modules, strings, types, + data, &pos, offset2); + } + break; + + case TYPE_TAG_INTERFACE: + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&data[*offset2]; + *offset2 += 4; + + iface->pointer = type->is_pointer; + iface->reserved = 0; + iface->tag = type->tag; + iface->reserved2 = 0; + iface->interface = find_entry (module, modules, type->interface); + + } + break; + + case TYPE_TAG_LIST: + case TYPE_TAG_SLIST: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 1; + + pos = *offset2 + 4; + *offset2 += 8; + + g_idl_node_build_metadata ((GIdlNode *)type->parameter_type1, + module, modules, strings, types, + data, &pos, offset2); + } + break; + + case TYPE_TAG_HASH: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 2; + + pos = *offset2 + 4; + *offset2 += 12; + + g_idl_node_build_metadata ((GIdlNode *)type->parameter_type1, + module, modules, strings, types, + data, &pos, offset2); + g_idl_node_build_metadata ((GIdlNode *)type->parameter_type2, + module, modules, strings, types, + data, &pos, offset2); + } + break; + + case TYPE_TAG_ERROR: + { + ErrorTypeBlob *blob = (ErrorTypeBlob *)&data[*offset2]; + gint i; + + blob->pointer = 1; + blob->reserved = 0; + blob->tag = type->tag; + blob->reserved2 = 0; + if (type->errors) + blob->n_domains = g_strv_length (type->errors); + else + blob->n_domains = 0; + + *offset2 = ALIGN_VALUE (*offset2 + 4 + 2 * blob->n_domains, 4); + for (i = 0; i < blob->n_domains; i++) + blob->domains[i] = find_entry (module, modules, type->errors[i]); + } + break; + + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + } + break; + + case G_IDL_NODE_FIELD: + { + GIdlNodeField *field = (GIdlNodeField *)node; + FieldBlob *blob; + + blob = (FieldBlob *)&data[*offset]; + *offset += 8; + + blob->name = write_string (node->name, strings, data, offset2); + blob->readable = field->readable; + blob->writable = field->writable; + blob->reserved = 0; + blob->bits = 0; + blob->struct_offset = field->offset; + + g_idl_node_build_metadata ((GIdlNode *)field->type, + module, modules, strings, types, + data, offset, offset2); + } + break; + + case G_IDL_NODE_PROPERTY: + { + GIdlNodeProperty *prop = (GIdlNodeProperty *)node; + PropertyBlob *blob = (PropertyBlob *)&data[*offset]; + *offset += 8; + + blob->name = write_string (node->name, strings, data, offset2); + blob->deprecated = prop->deprecated; + blob->readable = prop->readable; + blob->writable = prop->writable; + blob->construct = prop->construct; + blob->construct_only = prop->construct_only; + blob->reserved = 0; + + g_idl_node_build_metadata ((GIdlNode *)prop->type, + module, modules, strings, types, + data, offset, offset2); + } + break; + + case G_IDL_NODE_FUNCTION: + { + FunctionBlob *blob = (FunctionBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIdlNodeFunction *function = (GIdlNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += 16; + *offset2 += 8 + n * 12; + + blob->blob_type = BLOB_TYPE_FUNCTION; + blob->deprecated = function->deprecated; + blob->setter = function->is_setter; + blob->getter = function->is_getter; + blob->constructor = function->is_constructor; + blob->wraps_vfunc = function->wraps_vfunc; + blob->reserved = 0; + blob->index = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->symbol = write_string (function->symbol, strings, data, offset2); + blob->signature = signature; + + g_idl_node_build_metadata ((GIdlNode *)function->result->type, + module, modules, strings, types, + data, &signature, offset2); + + blob2->may_return_null = function->result->null_ok; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIdlNode *param = (GIdlNode *)l->data; + + g_idl_node_build_metadata (param, + module, modules, strings, types, + data, &signature, offset2); + } + } + break; + + case G_IDL_NODE_CALLBACK: + { + CallbackBlob *blob = (CallbackBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIdlNodeFunction *function = (GIdlNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += 12; + *offset2 += 8 + n * 12; + + blob->blob_type = BLOB_TYPE_CALLBACK; + blob->deprecated = function->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->signature = signature; + + g_idl_node_build_metadata ((GIdlNode *)function->result->type, + module, modules, strings, types, + data, &signature, offset2); + + blob2->may_return_null = function->result->null_ok; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIdlNode *param = (GIdlNode *)l->data; + + g_idl_node_build_metadata (param, + module, modules, strings, types, + data, &signature, offset2); + } + } + break; + + case G_IDL_NODE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIdlNodeSignal *signal = (GIdlNodeSignal *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (signal->parameters); + + *offset += 12; + *offset2 += 8 + n * 12; + + blob->deprecated = signal->deprecated; + blob->run_first = signal->run_first; + blob->run_last = signal->run_last; + blob->run_cleanup = signal->run_cleanup; + blob->no_recurse = signal->no_recurse; + blob->detailed = signal->detailed; + blob->action = signal->action; + blob->no_hooks = signal->no_hooks; + blob->has_class_closure = 0; /* FIXME */ + blob->true_stops_emit = 0; /* FIXME */ + blob->reserved = 0; + blob->class_closure = 0; /* FIXME */ + blob->name = write_string (node->name, strings, data, offset2); + blob->signature = signature; + + g_idl_node_build_metadata ((GIdlNode *)signal->result->type, + module, modules, strings, types, + data, &signature, offset2); + + blob2->may_return_null = signal->result->null_ok; + blob2->caller_owns_return_value = signal->result->transfer; + blob2->caller_owns_return_container = signal->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = signal->parameters; l; l = l->next) + { + GIdlNode *param = (GIdlNode *)l->data; + + g_idl_node_build_metadata (param, module, modules, strings, types, + data, &signature, offset2); + } + } + break; + + case G_IDL_NODE_VFUNC: + { + VFuncBlob *blob = (VFuncBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIdlNodeVFunc *vfunc = (GIdlNodeVFunc *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (vfunc->parameters); + + *offset += 16; + *offset2 += 8 + n * 12; + + blob->name = write_string (node->name, strings, data, offset2); + blob->must_chain_up = 0; /* FIXME */ + blob->must_be_implemented = 0; /* FIXME */ + blob->must_not_be_implemented = 0; /* FIXME */ + blob->class_closure = 0; /* FIXME */ + blob->reserved = 0; + + blob->struct_offset = vfunc->offset; + blob->reserved2 = 0; + blob->signature = signature; + + g_idl_node_build_metadata ((GIdlNode *)vfunc->result->type, + module, modules, strings, types, + data, &signature, offset2); + + blob2->may_return_null = vfunc->result->null_ok; + blob2->caller_owns_return_value = vfunc->result->transfer; + blob2->caller_owns_return_container = vfunc->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = vfunc->parameters; l; l = l->next) + { + GIdlNode *param = (GIdlNode *)l->data; + + g_idl_node_build_metadata (param, module, modules, strings, + types, data, &signature, offset2); + } + } + break; + + case G_IDL_NODE_PARAM: + { + ArgBlob *blob = (ArgBlob *)&data[*offset]; + GIdlNodeParam *param = (GIdlNodeParam *)node; + + *offset += 8; + + blob->name = write_string (node->name, strings, data, offset2); + blob->in = param->in; + blob->out = param->out; + blob->dipper = param->dipper; + blob->null_ok = param->null_ok; + blob->optional = param->optional; + blob->transfer_ownership = param->transfer; + blob->transfer_container_ownership = param->shallow_transfer; + blob->return_value = param->retval; + blob->reserved = 0; + + g_idl_node_build_metadata ((GIdlNode *)param->type, module, modules, + strings, types, data, offset, offset2); + } + break; + + case G_IDL_NODE_STRUCT: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIdlNodeStruct *struct_ = (GIdlNodeStruct *)node; + + blob->blob_type = BLOB_TYPE_STRUCT; + blob->deprecated = struct_->deprecated; + blob->unregistered = TRUE; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->gtype_name = 0; + blob->gtype_init = 0; + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += 20; + for (l = struct_->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FIELD) + { + blob->n_fields++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + for (l = struct_->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FUNCTION) + { + blob->n_methods++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + } + break; + + case G_IDL_NODE_BOXED: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIdlNodeBoxed *boxed = (GIdlNodeBoxed *)node; + + blob->blob_type = BLOB_TYPE_BOXED; + blob->deprecated = boxed->deprecated; + blob->unregistered = FALSE; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->gtype_name = write_string (boxed->gtype_name, strings, data, offset2); + blob->gtype_init = write_string (boxed->gtype_init, strings, data, offset2); + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += 20; + for (l = boxed->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FIELD) + { + blob->n_fields++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + for (l = boxed->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FUNCTION) + { + blob->n_methods++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + } + break; + + case G_IDL_NODE_UNION: + { + UnionBlob *blob = (UnionBlob *)&data[*offset]; + GIdlNodeUnion *union_ = (GIdlNodeUnion *)node; + + blob->blob_type = BLOB_TYPE_UNION; + blob->deprecated = union_->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + if (union_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = write_string (union_->gtype_name, strings, data, offset2); + blob->gtype_init = write_string (union_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + blob->n_fields = 0; + blob->n_functions = 0; + + blob->discriminator_offset = union_->discriminator_offset; + + if (union_->discriminator_type) + { + *offset += 24; + blob->discriminated = TRUE; + g_idl_node_build_metadata ((GIdlNode *)union_->discriminator_type, + module, modules, strings, types, + data, offset, offset2); + } + else + { + *offset += 28; + blob->discriminated = FALSE; + blob->discriminator_type.offset = 0; + } + + + for (l = union_->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FIELD) + { + blob->n_fields++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + for (l = union_->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FUNCTION) + { + blob->n_functions++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + if (union_->discriminator_type) + { + for (l = union_->discriminators; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + } + break; + + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + { + EnumBlob *blob = (EnumBlob *)&data[*offset]; + GIdlNodeEnum *enum_ = (GIdlNodeEnum *)node; + + *offset += 20; + + if (node->type == G_IDL_NODE_ENUM) + blob->blob_type = BLOB_TYPE_ENUM; + else + blob->blob_type = BLOB_TYPE_FLAGS; + + blob->deprecated = enum_->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + if (enum_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = write_string (enum_->gtype_name, strings, data, offset2); + blob->gtype_init = write_string (enum_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + blob->n_values = 0; + blob->reserved2 = 0; + + for (l = enum_->values; l; l = l->next) + { + GIdlNode *value = (GIdlNode *)l->data; + + blob->n_values++; + g_idl_node_build_metadata (value, module, modules, strings, types, + data, offset, offset2); + } + } + break; + + case G_IDL_NODE_OBJECT: + { + ObjectBlob *blob = (ObjectBlob *)&data[*offset]; + GIdlNodeInterface *object = (GIdlNodeInterface *)node; + + blob->blob_type = BLOB_TYPE_OBJECT; + blob->deprecated = object->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->gtype_name = write_string (object->gtype_name, strings, data, offset2); + blob->gtype_init = write_string (object->gtype_init, strings, data, offset2); + if (object->parent) + blob->parent = find_entry (module, modules, object->parent); + else + blob->parent = 0; + + blob->n_interfaces = 0; + blob->n_fields = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + + *offset += 32; + for (l = object->interfaces; l; l = l->next) + { + blob->n_interfaces++; + *(guint16*)&data[*offset] = find_entry (module, modules, (gchar *)l->data); + *offset += 2; + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FIELD) + { + blob->n_fields++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_PROPERTY) + { + blob->n_properties++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FUNCTION) + { + blob->n_methods++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_SIGNAL) + { + blob->n_signals++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_VFUNC) + { + blob->n_vfuncs++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = object->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_CONSTANT) + { + blob->n_constants++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + } + break; + + case G_IDL_NODE_INTERFACE: + { + InterfaceBlob *blob = (InterfaceBlob *)&data[*offset]; + GIdlNodeInterface *iface = (GIdlNodeInterface *)node; + + blob->blob_type = BLOB_TYPE_INTERFACE; + blob->deprecated = iface->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->gtype_name = write_string (iface->gtype_name, strings, data, offset2); + blob->gtype_init = write_string (iface->gtype_init, strings, data, offset2); + blob->n_prerequisites = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + + *offset += 28; + for (l = iface->prerequisites; l; l = l->next) + { + blob->n_prerequisites++; + *(guint16*)&data[*offset] = find_entry (module, modules, (gchar *)l->data); + *offset += 2; + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = iface->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_PROPERTY) + { + blob->n_properties++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = iface->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_FUNCTION) + { + blob->n_methods++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = iface->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_SIGNAL) + { + blob->n_signals++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = iface->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_VFUNC) + { + blob->n_vfuncs++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + + *offset = ALIGN_VALUE (*offset, 4); + for (l = iface->members; l; l = l->next) + { + GIdlNode *member = (GIdlNode *)l->data; + + if (member->type == G_IDL_NODE_CONSTANT) + { + blob->n_constants++; + g_idl_node_build_metadata (member, module, modules, strings, + types, data, offset, offset2); + } + } + } + break; + + + case G_IDL_NODE_VALUE: + { + GIdlNodeValue *value = (GIdlNodeValue *)node; + ValueBlob *blob = (ValueBlob *)&data[*offset]; + *offset += 12; + + blob->deprecated = value->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->value = value->value; + } + break; + + case G_IDL_NODE_ERROR_DOMAIN: + { + GIdlNodeErrorDomain *domain = (GIdlNodeErrorDomain *)node; + ErrorDomainBlob *blob = (ErrorDomainBlob *)&data[*offset]; + *offset += 16; + + blob->blob_type = BLOB_TYPE_ERROR_DOMAIN; + blob->deprecated = domain->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + blob->get_quark = write_string (domain->getquark, strings, data, offset2); + blob->error_codes = find_entry (module, modules, domain->codes); + blob->reserved2 = 0; + } + break; + + case G_IDL_NODE_CONSTANT: + { + GIdlNodeConstant *constant = (GIdlNodeConstant *)node; + ConstantBlob *blob = (ConstantBlob *)&data[*offset]; + guint32 pos; + + pos = *offset + 8; + *offset += 20; + + blob->blob_type = BLOB_TYPE_CONSTANT; + blob->deprecated = constant->deprecated; + blob->reserved = 0; + blob->name = write_string (node->name, strings, data, offset2); + + blob->offset = *offset2; + switch (constant->type->tag) + { + case TYPE_TAG_BOOLEAN: + blob->size = 4; + *(gboolean*)&data[blob->offset] = parse_boolean_value (constant->value); + break; + case TYPE_TAG_INT8: + blob->size = 1; + *(gint8*)&data[blob->offset] = (gint8) parse_int_value (constant->value); + break; + case TYPE_TAG_UINT8: + blob->size = 1; + *(guint8*)&data[blob->offset] = (guint8) parse_uint_value (constant->value); + break; + case TYPE_TAG_INT16: + blob->size = 2; + *(gint16*)&data[blob->offset] = (gint16) parse_int_value (constant->value); + break; + case TYPE_TAG_UINT16: + blob->size = 2; + *(guint16*)&data[blob->offset] = (guint16) parse_uint_value (constant->value); + break; + case TYPE_TAG_INT32: + blob->size = 4; + *(gint32*)&data[blob->offset] = (gint32) parse_int_value (constant->value); + break; + case TYPE_TAG_UINT32: + blob->size = 4; + *(guint32*)&data[blob->offset] = (guint32) parse_uint_value (constant->value); + break; + case TYPE_TAG_INT64: + blob->size = 8; + *(gint64*)&data[blob->offset] = (gint64) parse_int_value (constant->value); + break; + case TYPE_TAG_UINT64: + blob->size = 8; + *(guint64*)&data[blob->offset] = (guint64) parse_uint_value (constant->value); + break; + case TYPE_TAG_INT: + blob->size = sizeof (gint); + *(gint*)&data[blob->offset] = (gint) parse_int_value (constant->value); + break; + case TYPE_TAG_UINT: + blob->size = sizeof (guint); + *(gint*)&data[blob->offset] = (guint) parse_uint_value (constant->value); + break; + case TYPE_TAG_SSIZE: /* FIXME */ + case TYPE_TAG_LONG: + blob->size = sizeof (glong); + *(glong*)&data[blob->offset] = (glong) parse_int_value (constant->value); + break; + case TYPE_TAG_SIZE: /* FIXME */ + case TYPE_TAG_ULONG: + blob->size = sizeof (gulong); + *(gulong*)&data[blob->offset] = (gulong) parse_uint_value (constant->value); + break; + case TYPE_TAG_FLOAT: + blob->size = sizeof (gfloat); + *(gfloat*)&data[blob->offset] = (gfloat) parse_float_value (constant->value); + break; + case TYPE_TAG_DOUBLE: + blob->size = sizeof (gdouble); + *(gdouble*)&data[blob->offset] = (gdouble) parse_float_value (constant->value); + break; + case TYPE_TAG_UTF8: + case TYPE_TAG_FILENAME: + blob->size = strlen (constant->value) + 1; + memcpy (&data[blob->offset], constant->value, blob->size); + break; + } + *offset2 += ALIGN_VALUE (blob->size, 4); + + g_idl_node_build_metadata ((GIdlNode *)constant->type, module, modules, + strings, types, data, &pos, offset2); + } + break; + default: + g_assert_not_reached (); + } + + g_debug ("node %p type %d, offset %d -> %d, offset2 %d -> %d", + node, node->type, old_offset, *offset, old_offset2, *offset2); + + if (*offset2 - old_offset2 + *offset - old_offset > g_idl_node_get_full_size (node)) + g_error ("exceeding space reservation !!"); +} + +/* if str is already in the pool, return previous location, otherwise write str + * to the metadata at offset, put it in the pool and update offset. If the + * metadata is not large enough to hold the string, reallocate it. + */ +guint32 +write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset) +{ + gpointer value; + guint32 start; + + string_count += 1; + string_size += strlen (str); + + value = g_hash_table_lookup (strings, str); + + if (value) + return GPOINTER_TO_INT (value); + + unique_string_count += 1; + unique_string_size += strlen (str); + + g_hash_table_insert (strings, (gpointer)str, GINT_TO_POINTER (*offset)); + + start = *offset; + *offset = ALIGN_VALUE (start + strlen (str) + 1, 4); + + strcpy ((gchar*)&data[start], str); + + return start; +} + diff --git a/tools/gidlnode.h b/tools/gidlnode.h new file mode 100644 index 00000000..4ba97b1b --- /dev/null +++ b/tools/gidlnode.h @@ -0,0 +1,334 @@ +/* GObject introspection: Parsed IDL + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IDL_NODE_H__ +#define __G_IDL_NODE_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _GIdlNode GIdlNode; +typedef struct _GIdlNodeFunction GIdlNodeFunction; +typedef struct _GIdlNodeParam GIdlNodeParam; +typedef struct _GIdlNodeType GIdlNodeType; +typedef struct _GIdlNodeInterface GIdlNodeInterface; +typedef struct _GIdlNodeSignal GIdlNodeSignal; +typedef struct _GIdlNodeProperty GIdlNodeProperty; +typedef struct _GIdlNodeVFunc GIdlNodeVFunc; +typedef struct _GIdlNodeField GIdlNodeField; +typedef struct _GIdlNodeValue GIdlNodeValue; +typedef struct _GIdlNodeEnum GIdlNodeEnum; +typedef struct _GIdlNodeBoxed GIdlNodeBoxed; +typedef struct _GIdlNodeStruct GIdlNodeStruct; +typedef struct _GIdlNodeConstant GIdlNodeConstant; +typedef struct _GIdlNodeErrorDomain GIdlNodeErrorDomain; +typedef struct _GIdlNodeXRef GIdlNodeXRef; +typedef struct _GIdlNodeUnion GIdlNodeUnion; + +typedef enum +{ + G_IDL_NODE_INVALID, + G_IDL_NODE_FUNCTION, + G_IDL_NODE_CALLBACK, + G_IDL_NODE_STRUCT, + G_IDL_NODE_BOXED, + G_IDL_NODE_ENUM, + G_IDL_NODE_FLAGS, + G_IDL_NODE_OBJECT, + G_IDL_NODE_INTERFACE, + G_IDL_NODE_CONSTANT, + G_IDL_NODE_ERROR_DOMAIN, + G_IDL_NODE_UNION, + G_IDL_NODE_PARAM, + G_IDL_NODE_TYPE, + G_IDL_NODE_PROPERTY, + G_IDL_NODE_SIGNAL, + G_IDL_NODE_VALUE, + G_IDL_NODE_VFUNC, + G_IDL_NODE_FIELD, + G_IDL_NODE_XREF +} GIdlNodeTypeId; + +struct _GIdlNode +{ + GIdlNodeTypeId type; + gchar *name; +}; + +struct _GIdlNodeXRef +{ + GIdlNode node; + + gchar *namespace; +}; + +struct _GIdlNodeFunction +{ + GIdlNode node; + + gboolean deprecated; + + gboolean is_method; + gboolean is_setter; + gboolean is_getter; + gboolean is_constructor; + gboolean wraps_vfunc; + + gchar *symbol; + + GIdlNodeParam *result; + GList *parameters; +}; + +struct _GIdlNodeType +{ + GIdlNode node; + + gboolean is_pointer; + gboolean is_basic; + gboolean is_array; + gboolean is_glist; + gboolean is_gslist; + gboolean is_ghashtable; + gboolean is_interface; + gboolean is_error; + gint tag; + + gchar *unparsed; + + gboolean zero_terminated; + gboolean has_length; + gint length; + + GIdlNodeType *parameter_type1; + GIdlNodeType *parameter_type2; + + gchar *interface; + gchar **errors; +}; + +struct _GIdlNodeParam +{ + GIdlNode node; + + gboolean in; + gboolean out; + gboolean dipper; + gboolean optional; + gboolean retval; + gboolean null_ok; + gboolean transfer; + gboolean shallow_transfer; + + GIdlNodeType *type; +}; + +struct _GIdlNodeProperty +{ + GIdlNode node; + + gboolean deprecated; + + gchar *name; + gboolean readable; + gboolean writable; + gboolean construct; + gboolean construct_only; + + GIdlNodeType *type; +}; + +struct _GIdlNodeSignal +{ + GIdlNode node; + + gboolean deprecated; + + gboolean run_first; + gboolean run_last; + gboolean run_cleanup; + gboolean no_recurse; + gboolean detailed; + gboolean action; + gboolean no_hooks; + + gboolean has_class_closure; + gboolean true_stops_emit; + + gint class_closure; + + GList *parameters; + GIdlNodeParam *result; +}; + +struct _GIdlNodeVFunc +{ + GIdlNode node; + + gboolean must_chain_up; + gboolean must_be_implemented; + gboolean must_not_be_implemented; + gboolean is_class_closure; + + GList *parameters; + GIdlNodeParam *result; + + gint offset; +}; + +struct _GIdlNodeField +{ + GIdlNode node; + + gboolean readable; + gboolean writable; + gint bits; + gint offset; + + GIdlNodeType *type; +}; + +struct _GIdlNodeInterface +{ + GIdlNode node; + + gboolean deprecated; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *parent; + + GList *interfaces; + GList *prerequisites; + + GList *members; +}; + +struct _GIdlNodeValue +{ + GIdlNode node; + + gboolean deprecated; + + guint32 value; +}; + +struct _GIdlNodeConstant +{ + GIdlNode node; + + gboolean deprecated; + + GIdlNodeType *type; + + gchar *value; +}; + +struct _GIdlNodeEnum +{ + GIdlNode node; + + gboolean deprecated; + + gchar *gtype_name; + gchar *gtype_init; + + GList *values; +}; + +struct _GIdlNodeBoxed +{ + GIdlNode node; + + gboolean deprecated; + + gchar *gtype_name; + gchar *gtype_init; + + GList *members; +}; + +struct _GIdlNodeStruct +{ + GIdlNode node; + + gboolean deprecated; + + GList *members; +}; + +struct _GIdlNodeUnion +{ + GIdlNode node; + + gboolean deprecated; + + GList *members; + GList *discriminators; + + gchar *gtype_name; + gchar *gtype_init; + + gint discriminator_offset; + GIdlNodeType *discriminator_type; +}; + + +struct _GIdlNodeErrorDomain +{ + GIdlNode node; + + gboolean deprecated; + + gchar *name; + gchar *getquark; + gchar *codes; +}; + + +GIdlNode *g_idl_node_new (GIdlNodeTypeId type); +void g_idl_node_free (GIdlNode *node); +guint32 g_idl_node_get_size (GIdlNode *node); +guint32 g_idl_node_get_full_size (GIdlNode *node); +void g_idl_node_build_metadata (GIdlNode *node, + GIdlModule *module, + GList *modules, + GHashTable *strings, + GHashTable *types, + guchar *data, + guint32 *offset, + guint32 *offset2); +int g_idl_node_cmp (GIdlNode *node, + GIdlNode *other); +void g_idl_node_add_member (GIdlNode *node, + GIdlNodeFunction *member); +guint32 write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset); + +void init_stats (void); +void dump_stats (void); + +G_END_DECLS + +#endif /* __G_IDL_NODE_H__ */ diff --git a/tools/gidlparser.c b/tools/gidlparser.c new file mode 100644 index 00000000..3d529261 --- /dev/null +++ b/tools/gidlparser.c @@ -0,0 +1,2054 @@ +/* GObject introspection: A parser for the XML IDL format + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include "gidlmodule.h" +#include "gidlnode.h" +#include "gmetadata.h" + +typedef enum +{ + STATE_START, + STATE_END, + STATE_ROOT, + STATE_NAMESPACE, + STATE_FUNCTION, + STATE_PARAMETERS, + STATE_OBJECT, + STATE_INTERFACE, + STATE_IMPLEMENTS, + STATE_REQUIRES, + STATE_ENUM, + STATE_BOXED, + STATE_STRUCT, + STATE_SIGNAL, + STATE_ERRORDOMAIN, + STATE_UNION +} ParseState; + +typedef struct _ParseContext ParseContext; +struct _ParseContext +{ + ParseState state; + ParseState prev_state; + + GList *modules; + + GIdlModule *current_module; + GIdlNode *current_node; +}; + +#define MISSING_ATTRIBUTE(error,element,attribute) \ + g_set_error (error, \ + G_MARKUP_ERROR, \ + G_MARKUP_ERROR_INVALID_CONTENT, \ + "The attribute '%s' on the element '%s' must be specified", \ + attribute, element) + +static const gchar * +find_attribute (const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + gint i; + + for (i = 0; attribute_names[i] != NULL; i++) + if (strcmp (attribute_names[i], name) == 0) + return attribute_values[i]; + + return 0; +} + +static GIdlNodeType * +parse_type_internal (gchar *str, gchar **rest) +{ + gint i; + + static struct { + const gchar *str; + gint tag; + gboolean pointer; + } basic[] = { + { "void", TYPE_TAG_VOID, 0 }, + { "gpointer", TYPE_TAG_VOID, 1 }, + { "gboolean", TYPE_TAG_BOOLEAN, 0 }, +#if 0 + { "char", TYPE_TAG_INT8, 0 }, + { "gchar", TYPE_TAG_INT8, 0 }, + { "guchar", TYPE_TAG_UINT8, 0 }, +#endif + { "int8_t", TYPE_TAG_INT8, 0 }, + { "int8", TYPE_TAG_INT8, 0 }, + { "gint8", TYPE_TAG_INT8, 0 }, + { "uint8_t", TYPE_TAG_UINT8, 0 }, + { "uint8", TYPE_TAG_UINT8, 0 }, + { "guint8", TYPE_TAG_UINT8, 0 }, + { "int16_t", TYPE_TAG_INT16, 0 }, + { "int16", TYPE_TAG_INT16, 0 }, + { "gint16", TYPE_TAG_INT16, 0 }, + { "uint16_t", TYPE_TAG_UINT16, 0 }, + { "uint16", TYPE_TAG_UINT16, 0 }, + { "guint16", TYPE_TAG_UINT16, 0 }, + { "int32_t", TYPE_TAG_INT32, 0 }, + { "int32", TYPE_TAG_INT32, 0 }, + { "gint32", TYPE_TAG_INT32, 0 }, + { "uint32_t", TYPE_TAG_UINT32, 0 }, + { "uint32", TYPE_TAG_UINT32, 0 }, + { "guint32", TYPE_TAG_UINT32, 0 }, + { "int64_t", TYPE_TAG_INT64, 0 }, + { "int64", TYPE_TAG_INT64, 0 }, + { "gint64", TYPE_TAG_INT64, 0 }, + { "uint64_t", TYPE_TAG_UINT64, 0 }, + { "uint64", TYPE_TAG_UINT64, 0 }, + { "guint64", TYPE_TAG_UINT64, 0 }, + { "int", TYPE_TAG_INT, 0 }, + { "gint", TYPE_TAG_INT, 0 }, + { "uint", TYPE_TAG_UINT, 0 }, + { "guint", TYPE_TAG_UINT, 0 }, + { "long", TYPE_TAG_LONG, 0 }, + { "glong", TYPE_TAG_LONG, 0 }, + { "ulong", TYPE_TAG_ULONG, 0 }, + { "gulong", TYPE_TAG_ULONG, 0 }, + { "ssize_t", TYPE_TAG_SSIZE, 0 }, + { "gssize", TYPE_TAG_SSIZE, 0 }, + { "size_t", TYPE_TAG_SIZE, 0 }, + { "gsize", TYPE_TAG_SIZE, 0 }, + { "float", TYPE_TAG_FLOAT, 0 }, + { "gfloat", TYPE_TAG_FLOAT, 0 }, + { "double", TYPE_TAG_DOUBLE, 0 }, + { "gdouble", TYPE_TAG_DOUBLE, 0 }, + { "utf8", TYPE_TAG_UTF8, 1 }, + { "gchar*", TYPE_TAG_UTF8, 1 }, + { "filename", TYPE_TAG_FILENAME,1 } + }; + + gint n_basic = G_N_ELEMENTS (basic); + gchar *start, *end; + + GIdlNodeType *type; + + type = (GIdlNodeType *)g_idl_node_new (G_IDL_NODE_TYPE); + + str = g_strstrip (str); + + type->unparsed = g_strdup (str); + + *rest = str; + for (i = 0; i < n_basic; i++) + { + if (g_str_has_prefix (*rest, basic[i].str)) + { + type->is_basic = TRUE; + type->tag = basic[i].tag; + type->is_pointer = basic[i].pointer; + + *rest += strlen(basic[i].str); + *rest = g_strchug (*rest); + if (**rest == '*' && !type->is_pointer) + { + type->is_pointer = TRUE; + (*rest)++; + } + + break; + } + } + + if (i < n_basic) + /* found a basic type */; + else if (g_str_has_prefix (*rest, "GList") || + g_str_has_prefix (*rest, "GSList")) + { + if (g_str_has_prefix (*rest, "GList")) + { + type->tag = TYPE_TAG_LIST; + type->is_glist = TRUE; + type->is_pointer = TRUE; + *rest += strlen ("GList"); + } + else + { + type->tag = TYPE_TAG_SLIST; + type->is_gslist = TRUE; + type->is_pointer = TRUE; + *rest += strlen ("GSList"); + } + + *rest = g_strchug (*rest); + + if (**rest == '<') + { + (*rest)++; + + type->parameter_type1 = parse_type_internal (*rest, rest); + if (type->parameter_type1 == NULL) + goto error; + + *rest = g_strchug (*rest); + + if ((*rest)[0] != '>') + goto error; + (*rest)++; + } + } + else if (g_str_has_prefix (*rest, "GHashTable")) + { + type->tag = TYPE_TAG_HASH; + type->is_ghashtable = TRUE; + type->is_pointer = TRUE; + *rest += strlen ("GHashTable"); + + *rest = g_strchug (*rest); + + if (**rest == '<') + { + (*rest)++; + + type->parameter_type1 = parse_type_internal (*rest, rest); + if (type->parameter_type1 == NULL) + goto error; + + *rest = g_strchug (*rest); + + if ((*rest)[0] != ',') + goto error; + (*rest)++; + + type->parameter_type2 = parse_type_internal (*rest, rest); + if (type->parameter_type2 == NULL) + goto error; + + if ((*rest)[0] != '>') + goto error; + (*rest)++; + } + } + else if (g_str_has_prefix (*rest, "GError")) + { + type->tag = TYPE_TAG_ERROR; + type->is_error = TRUE; + type->is_pointer = TRUE; + *rest += strlen ("GError"); + + *rest = g_strchug (*rest); + + if (**rest == '<') + { + (*rest)++; + + end = strchr (*rest, '>'); + str = g_strndup (*rest, end - *rest); + type->errors = g_strsplit (str, ",", 0); + g_free (str); + + *rest = end + 1; + } + } + else + { + type->tag = TYPE_TAG_INTERFACE; + type->is_interface = TRUE; + start = *rest; + + /* must be an interface type */ + while (g_ascii_isalnum (**rest) || + **rest == '.' || + **rest == '-' || + **rest == '_' || + **rest == ':') + (*rest)++; + + type->interface = g_strndup (start, *rest - start); + + *rest = g_strchug (*rest); + if (**rest == '*') + { + type->is_pointer = TRUE; + (*rest)++; + } + } + + *rest = g_strchug (*rest); + if (g_str_has_prefix (*rest, "[")) + { + GIdlNodeType *array; + + array = (GIdlNodeType *)g_idl_node_new (G_IDL_NODE_TYPE); + + array->tag = TYPE_TAG_ARRAY; + array->is_pointer = TRUE; + array->is_array = TRUE; + + array->parameter_type1 = type; + + array->zero_terminated = FALSE; + array->has_length = FALSE; + array->length = 0; + + if (!g_str_has_prefix (*rest, "[]")) + { + gchar *end, *str, **opts; + + end = strchr (*rest, ']'); + str = g_strndup (*rest + 1, (end - *rest) - 1); + opts = g_strsplit (str, ",", 0); + + *rest = end + 1; + + for (i = 0; opts[i]; i++) + { + gchar **vals; + + vals = g_strsplit (opts[i], "=", 0); + + if (strcmp (vals[0], "zero-terminated") == 0) + array->zero_terminated = (strcmp (vals[1], "1") == 0); + else if (strcmp (vals[0], "length") == 0) + { + array->has_length = TRUE; + array->length = atoi (vals[1]); + } + + g_strfreev (vals); + } + + g_free (str); + g_strfreev (opts); + } + + type = array; + } + + return type; + + error: + g_idl_node_free ((GIdlNode *)type); + + return NULL; +} + +static GIdlNodeType * +parse_type (const gchar *type) +{ + gchar *str; + gchar *rest; + GIdlNodeType *node; + + str = g_strdup (type); + node = parse_type_internal (str, &rest); + g_free (str); + + return node; +} + +static gboolean +start_boxed (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "boxed") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("type-name", attribute_names, attribute_values); + typeinit = find_attribute ("get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (typename == NULL) + MISSING_ATTRIBUTE (error, element_name, "type-name"); + else if (typeinit == NULL) + MISSING_ATTRIBUTE (error, element_name, "get-type"); + else + { + GIdlNodeBoxed *boxed; + + boxed = (GIdlNodeBoxed *) g_idl_node_new (G_IDL_NODE_BOXED); + + ((GIdlNode *)boxed)->name = g_strdup (name); + boxed->gtype_name = g_strdup (typename); + boxed->gtype_init = g_strdup (typeinit); + if (deprecated && strcmp (deprecated, "1") == 0) + boxed->deprecated = TRUE; + else + boxed->deprecated = FALSE; + + ctx->current_node = (GIdlNode *)boxed; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, boxed); + + ctx->state = STATE_BOXED; + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +start_function (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if ((ctx->state == STATE_NAMESPACE && + (strcmp (element_name, "function") == 0 || + strcmp (element_name, "callback") == 0)) || + ((ctx->state == STATE_OBJECT || + ctx->state == STATE_INTERFACE || + ctx->state == STATE_BOXED || + ctx->state == STATE_STRUCT || + ctx->state == STATE_UNION) && + strcmp (element_name, "method") == 0) || + ((ctx->state == STATE_OBJECT || + ctx->state == STATE_BOXED) && + strcmp (element_name, "constructor") == 0)) + { + const gchar *name; + const gchar *symbol; + const gchar *deprecated; + const gchar *type; + + name = find_attribute ("name", attribute_names, attribute_values); + symbol = find_attribute ("symbol", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + type = find_attribute ("type", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (strcmp (element_name, "callback") != 0 && symbol == NULL) + MISSING_ATTRIBUTE (error, element_name, "symbol"); + else + { + GIdlNodeFunction *function; + + function = (GIdlNodeFunction *) g_idl_node_new (G_IDL_NODE_FUNCTION); + + ((GIdlNode *)function)->name = g_strdup (name); + function->symbol = g_strdup (symbol); + function->parameters = NULL; + if (deprecated && strcmp (deprecated, "1") == 0) + function->deprecated = TRUE; + else + function->deprecated = FALSE; + + if (strcmp (element_name, "method") == 0 || + strcmp (element_name, "constructor") == 0) + { + function->is_method = TRUE; + + if (type && strcmp (type, "setter") == 0) + function->is_setter = TRUE; + else if (type && strcmp (type, "getter") == 0) + function->is_getter = TRUE; + + if (strcmp (element_name, "constructor") == 0) + function->is_constructor = TRUE; + else + function->is_constructor = FALSE; + } + else + { + function->is_method = FALSE; + function->is_setter = FALSE; + function->is_getter = FALSE; + function->is_constructor = FALSE; + if (strcmp (element_name, "callback") == 0) + ((GIdlNode *)function)->type = G_IDL_NODE_CALLBACK; + } + + if (ctx->current_node == NULL) + { + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, function); + } + else + switch (ctx->current_node->type) + { + case G_IDL_NODE_INTERFACE: + case G_IDL_NODE_OBJECT: + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, function); + } + break; + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed; + + boxed = (GIdlNodeBoxed *)ctx->current_node; + boxed->members = g_list_append (boxed->members, function); + } + break; + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_; + + struct_ = (GIdlNodeStruct *)ctx->current_node; + struct_->members = g_list_append (struct_->members, function); } + break; + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_; + + union_ = (GIdlNodeUnion *)ctx->current_node; + union_->members = g_list_append (union_->members, function); + } + break; + default: + g_assert_not_reached (); + } + + ctx->current_node = (GIdlNode *)function; + ctx->state = STATE_FUNCTION; + + return TRUE; + } + } + + return FALSE; +} + +static gboolean +start_parameter (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "parameter") == 0 && + ctx->state == STATE_PARAMETERS) + { + const gchar *type; + const gchar *name; + const gchar *direction; + const gchar *retval; + const gchar *dipper; + const gchar *optional; + const gchar *nullok; + const gchar *transfer; + + type = find_attribute ("type", attribute_names, attribute_values); + name = find_attribute ("name", attribute_names, attribute_values); + direction = find_attribute ("direction", attribute_names, attribute_values); + retval = find_attribute ("retval", attribute_names, attribute_values); + dipper = find_attribute ("dipper", attribute_names, attribute_values); + optional = find_attribute ("optional", attribute_names, attribute_values); + nullok = find_attribute ("null-ok", attribute_names, attribute_values); + transfer = find_attribute ("transfer", attribute_names, attribute_values); + + if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeParam *param; + + param = (GIdlNodeParam *)g_idl_node_new (G_IDL_NODE_PARAM); + + if (direction && strcmp (direction, "out") == 0) + { + param->in = FALSE; + param->out = TRUE; + } + else if (direction && strcmp (direction, "inout") == 0) + { + param->in = TRUE; + param->out = TRUE; + } + else + { + param->in = TRUE; + param->out = FALSE; + } + + if (retval && strcmp (retval, "1") == 0) + param->retval = TRUE; + else + param->retval = FALSE; + + if (dipper && strcmp (dipper, "1") == 0) + param->dipper = TRUE; + else + param->dipper = FALSE; + + if (optional && strcmp (optional, "1") == 0) + param->optional = TRUE; + else + param->optional = FALSE; + + if (nullok && strcmp (nullok, "1") == 0) + param->null_ok = TRUE; + else + param->null_ok = FALSE; + + if (transfer && strcmp (transfer, "none") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = FALSE; + } + else if (transfer && strcmp (transfer, "shallow") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = TRUE; + } + else + { + param->transfer = TRUE; + param->shallow_transfer = FALSE; + } + + ((GIdlNode *)param)->name = g_strdup (name); + param->type = parse_type (type); + + switch (ctx->current_node->type) + { + case G_IDL_NODE_FUNCTION: + case G_IDL_NODE_CALLBACK: + { + GIdlNodeFunction *func; + + func = (GIdlNodeFunction *)ctx->current_node; + func->parameters = g_list_append (func->parameters, param); + } + break; + case G_IDL_NODE_SIGNAL: + { + GIdlNodeSignal *signal; + + signal = (GIdlNodeSignal *)ctx->current_node; + signal->parameters = g_list_append (signal->parameters, param); + } + break; + case G_IDL_NODE_VFUNC: + { + GIdlNodeVFunc *vfunc; + + vfunc = (GIdlNodeVFunc *)ctx->current_node; + vfunc->parameters = g_list_append (vfunc->parameters, param); + } + break; + default: + g_assert_not_reached (); + } + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +start_field (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "field") == 0 && + (ctx->state == STATE_OBJECT || + ctx->state == STATE_BOXED || + ctx->state == STATE_STRUCT || + ctx->state == STATE_UNION)) + { + const gchar *name; + const gchar *type; + const gchar *readable; + const gchar *writable; + const gchar *bits; + const gchar *branch; + const gchar *offset; + + name = find_attribute ("name", attribute_names, attribute_values); + type = find_attribute ("type", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + bits = find_attribute ("bits", attribute_names, attribute_values); + branch = find_attribute ("branch", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else + { + GIdlNodeField *field; + + field = (GIdlNodeField *)g_idl_node_new (G_IDL_NODE_FIELD); + ((GIdlNode *)field)->name = g_strdup (name); + if (readable && strcmp (readable, "1") == 0) + field->readable = TRUE; + else + field->readable = FALSE; + + if (writable && strcmp (writable, "1") == 0) + field->writable = TRUE; + else + field->writable = FALSE; + + if (bits) + field->bits = atoi (bits); + else + field->bits = 0; + + if (offset) + field->offset = atoi (offset); + else + field->offset = 0; + + field->type = parse_type (type); + + switch (ctx->current_node->type) + { + case G_IDL_NODE_OBJECT: + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, field); + } + break; + case G_IDL_NODE_BOXED: + { + GIdlNodeBoxed *boxed; + + boxed = (GIdlNodeBoxed *)ctx->current_node; + boxed->members = g_list_append (boxed->members, field); + } + break; + case G_IDL_NODE_STRUCT: + { + GIdlNodeStruct *struct_; + + struct_ = (GIdlNodeStruct *)ctx->current_node; + struct_->members = g_list_append (struct_->members, field); + } + break; + case G_IDL_NODE_UNION: + { + GIdlNodeUnion *union_; + + union_ = (GIdlNodeUnion *)ctx->current_node; + union_->members = g_list_append (union_->members, field); + if (branch) + { + GIdlNodeConstant *constant; + + constant = (GIdlNodeConstant *) g_idl_node_new (G_IDL_NODE_CONSTANT); + ((GIdlNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (branch); + constant->type = union_->discriminator_type; + constant->deprecated = FALSE; + + union_->discriminators = g_list_append (union_->discriminators, constant); + } + } + break; + default: + g_assert_not_reached (); + } + } + return TRUE; + } + + return FALSE; +} + +static gboolean +start_enum (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if ((strcmp (element_name, "enum") == 0 && ctx->state == STATE_NAMESPACE) || + (strcmp (element_name, "flags") == 0 && ctx->state == STATE_NAMESPACE)) + { + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("type-name", attribute_names, attribute_values); + typeinit = find_attribute ("get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeEnum *enum_; + + if (strcmp (element_name, "enum") == 0) + enum_ = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_ENUM); + else + enum_ = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_FLAGS); + ((GIdlNode *)enum_)->name = g_strdup (name); + enum_->gtype_name = g_strdup (typename); + enum_->gtype_init = g_strdup (typeinit); + if (deprecated && strcmp (deprecated, "1") == 0) + enum_->deprecated = TRUE; + else + enum_->deprecated = FALSE; + + ctx->current_node = (GIdlNode *) enum_; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, enum_); + + ctx->state = STATE_ENUM; + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_property (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "property") == 0 && + (ctx->state == STATE_OBJECT || + ctx->state == STATE_INTERFACE)) + { + const gchar *name; + const gchar *type; + const gchar *readable; + const gchar *writable; + const gchar *construct; + const gchar *construct_only; + + name = find_attribute ("name", attribute_names, attribute_values); + type = find_attribute ("type", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + construct = find_attribute ("construct", attribute_names, attribute_values); + construct_only = find_attribute ("construct-only", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else + { + GIdlNodeProperty *property; + GIdlNodeInterface *iface; + + property = (GIdlNodeProperty *) g_idl_node_new (G_IDL_NODE_PROPERTY); + + ((GIdlNode *)property)->name = g_strdup (name); + + if (readable && strcmp (readable, "1") == 0) + property->readable = TRUE; + else + property->readable = FALSE; + if (writable && strcmp (writable, "1") == 0) + property->writable = TRUE; + else + property->writable = FALSE; + if (construct && strcmp (construct, "1") == 0) + property->construct = TRUE; + else + property->construct = FALSE; + if (construct_only && strcmp (construct_only, "1") == 0) + property->construct_only = TRUE; + else + property->construct_only = FALSE; + + property->type = parse_type (type); + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, property); + } + + return TRUE; + } + return FALSE; +} + +static gint +parse_value (const gchar *str) +{ + gchar *shift_op; + + /* FIXME just a quick hack */ + shift_op = strstr (str, "<<"); + + if (shift_op) + { + gint base, shift; + + base = strtol (str, NULL, 10); + shift = strtol (shift_op + 3, NULL, 10); + + return base << shift; + } + else + return strtol (str, NULL, 10); + + return 0; +} + +static gboolean +start_member (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "member") == 0 && + ctx->state == STATE_ENUM) + { + const gchar *name; + const gchar *value; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeEnum *enum_; + GIdlNodeValue *value_; + + value_ = (GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE); + + ((GIdlNode *)value_)->name = g_strdup (name); + + value_->value = parse_value (value); + + if (deprecated && strcmp (deprecated, "1") == 0) + value_->deprecated = TRUE; + else + value_->deprecated = FALSE; + + enum_ = (GIdlNodeEnum *)ctx->current_node; + enum_->values = g_list_append (enum_->values, value_); + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_constant (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "constant") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_OBJECT || + ctx->state == STATE_INTERFACE)) + { + const gchar *name; + const gchar *type; + const gchar *value; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + type = find_attribute ("type", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else if (value == NULL) + MISSING_ATTRIBUTE (error, element_name, "value"); + else + { + GIdlNodeConstant *constant; + + constant = (GIdlNodeConstant *) g_idl_node_new (G_IDL_NODE_CONSTANT); + + ((GIdlNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (value); + + constant->type = parse_type (type); + + if (deprecated && strcmp (deprecated, "1") == 0) + constant->deprecated = TRUE; + else + constant->deprecated = FALSE; + + if (ctx->state == STATE_NAMESPACE) + { + ctx->current_node = (GIdlNode *) constant; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, constant); + } + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, constant); + } + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_errordomain (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "errordomain") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *getquark; + const gchar *codes; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + getquark = find_attribute ("get-quark", attribute_names, attribute_values); + codes = find_attribute ("codes", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (getquark == NULL) + MISSING_ATTRIBUTE (error, element_name, "getquark"); + else if (codes == NULL) + MISSING_ATTRIBUTE (error, element_name, "codes"); + else + { + GIdlNodeErrorDomain *domain; + + domain = (GIdlNodeErrorDomain *) g_idl_node_new (G_IDL_NODE_ERROR_DOMAIN); + + ((GIdlNode *)domain)->name = g_strdup (name); + domain->getquark = g_strdup (getquark); + domain->codes = g_strdup (codes); + + if (deprecated && strcmp (deprecated, "1") == 0) + domain->deprecated = TRUE; + else + domain->deprecated = FALSE; + + ctx->current_node = (GIdlNode *) domain; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, domain); + + ctx->state = STATE_ERRORDOMAIN; + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_interface (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "interface") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("type-name", attribute_names, attribute_values); + typeinit = find_attribute ("get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (typename == NULL) + MISSING_ATTRIBUTE (error, element_name, "type-name"); + else if (typeinit == NULL) + MISSING_ATTRIBUTE (error, element_name, "get-type"); + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_INTERFACE); + ((GIdlNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + if (deprecated && strcmp (deprecated, "1") == 0) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + ctx->current_node = (GIdlNode *) iface; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + ctx->state = STATE_INTERFACE; + + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_object (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "object") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *parent; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + parent = find_attribute ("parent", attribute_names, attribute_values); + typename = find_attribute ("type-name", attribute_names, attribute_values); + typeinit = find_attribute ("get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (typename == NULL) + MISSING_ATTRIBUTE (error, element_name, "type-name"); + else if (typeinit == NULL) + MISSING_ATTRIBUTE (error, element_name, "get-type"); + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_OBJECT); + ((GIdlNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + iface->parent = g_strdup (parent); + if (deprecated && strcmp (deprecated, "1") == 0) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + ctx->current_node = (GIdlNode *) iface; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + ctx->state = STATE_OBJECT; + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_return_type (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "return-type") == 0 && + ctx->state == STATE_FUNCTION) + { + const gchar *type; + const gchar *nullok; + const gchar *transfer; + + type = find_attribute ("type", attribute_names, attribute_values); + nullok = find_attribute ("null-ok", attribute_names, attribute_values); + transfer = find_attribute ("transfer", attribute_names, attribute_values); + if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else + { + GIdlNodeParam *param; + + param = (GIdlNodeParam *)g_idl_node_new (G_IDL_NODE_PARAM); + param->in = FALSE; + param->out = FALSE; + param->retval = TRUE; + if (nullok && strcmp (nullok, "1") == 0) + param->null_ok = TRUE; + else + param->null_ok = FALSE; + if (transfer && strcmp (transfer, "none") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = FALSE; + } + else if (transfer && strcmp (transfer, "shallow") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = TRUE; + } + else + { + param->transfer = TRUE; + param->shallow_transfer = FALSE; + } + + param->type = parse_type (type); + + switch (ctx->current_node->type) + { + case G_IDL_NODE_FUNCTION: + case G_IDL_NODE_CALLBACK: + { + GIdlNodeFunction *func = (GIdlNodeFunction *)ctx->current_node; + func->result = param; + } + break; + case G_IDL_NODE_SIGNAL: + { + GIdlNodeSignal *signal = (GIdlNodeSignal *)ctx->current_node; + signal->result = param; + } + break; + case G_IDL_NODE_VFUNC: + { + GIdlNodeVFunc *vfunc = (GIdlNodeVFunc *)ctx->current_node; + vfunc->result = param; + } + break; + default: + g_assert_not_reached (); + } + } + + return TRUE; + } + + return FALSE; +} + +static gboolean +start_signal (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "signal") == 0 && + (ctx->state == STATE_OBJECT || + ctx->state == STATE_INTERFACE)) + { + const gchar *name; + const gchar *when; + const gchar *no_recurse; + const gchar *detailed; + const gchar *action; + const gchar *no_hooks; + const gchar *has_class_closure; + + name = find_attribute ("name", attribute_names, attribute_values); + when = find_attribute ("when", attribute_names, attribute_values); + no_recurse = find_attribute ("no-recurse", attribute_names, attribute_values); + detailed = find_attribute ("detailed", attribute_names, attribute_values); + action = find_attribute ("action", attribute_names, attribute_values); + no_hooks = find_attribute ("no-hooks", attribute_names, attribute_values); + has_class_closure = find_attribute ("has-class-closure", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else if (when == NULL) + MISSING_ATTRIBUTE (error, element_name, "when"); + else + { + GIdlNodeInterface *iface; + GIdlNodeSignal *signal; + + signal = (GIdlNodeSignal *)g_idl_node_new (G_IDL_NODE_SIGNAL); + + ((GIdlNode *)signal)->name = g_strdup (name); + + signal->run_first = FALSE; + signal->run_last = FALSE; + signal->run_cleanup = FALSE; + if (strcmp (when, "FIRST") == 0) + signal->run_first = TRUE; + else if (strcmp (when, "LAST") == 0) + signal->run_last = TRUE; + else + signal->run_cleanup = TRUE; + + if (no_recurse && strcmp (no_recurse, "1") == 0) + signal->no_recurse = TRUE; + else + signal->no_recurse = FALSE; + if (detailed && strcmp (detailed, "1") == 0) + signal->detailed = TRUE; + else + signal->detailed = FALSE; + if (action && strcmp (action, "1") == 0) + signal->action = TRUE; + else + signal->action = FALSE; + if (no_hooks && strcmp (no_hooks, "1") == 0) + signal->no_hooks = TRUE; + else + signal->no_hooks = FALSE; + if (has_class_closure && strcmp (has_class_closure, "1") == 0) + signal->has_class_closure = TRUE; + else + signal->has_class_closure = FALSE; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, signal); + + ctx->current_node = (GIdlNode *)signal; + ctx->state = STATE_FUNCTION; + } + + return TRUE; + } + return FALSE; +} + +static gboolean +start_vfunc (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "vfunc") == 0 && + (ctx->state == STATE_OBJECT || + ctx->state == STATE_INTERFACE)) + { + const gchar *name; + const gchar *must_chain_up; + const gchar *override; + const gchar *is_class_closure; + const gchar *offset; + + name = find_attribute ("name", attribute_names, attribute_values); + must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values); + override = find_attribute ("override", attribute_names, attribute_values); + is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeInterface *iface; + GIdlNodeVFunc *vfunc; + + vfunc = (GIdlNodeVFunc *)g_idl_node_new (G_IDL_NODE_VFUNC); + + ((GIdlNode *)vfunc)->name = g_strdup (name); + + if (must_chain_up && strcmp (must_chain_up, "1") == 0) + vfunc->must_chain_up = TRUE; + else + vfunc->must_chain_up = FALSE; + + if (override && strcmp (override, "always") == 0) + { + vfunc->must_be_implemented = TRUE; + vfunc->must_not_be_implemented = FALSE; + } + else if (override && strcmp (override, "never") == 0) + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = TRUE; + } + else + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = FALSE; + } + + if (is_class_closure && strcmp (is_class_closure, "1") == 0) + vfunc->is_class_closure = TRUE; + else + vfunc->is_class_closure = FALSE; + + if (offset) + vfunc->offset = atoi (offset); + else + vfunc->offset = 0; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface->members = g_list_append (iface->members, vfunc); + + ctx->current_node = (GIdlNode *)vfunc; + ctx->state = STATE_FUNCTION; + } + + return TRUE; + } + return FALSE; +} + + +static gboolean +start_struct (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "struct") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *deprecated; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeStruct *struct_; + + struct_ = (GIdlNodeStruct *) g_idl_node_new (G_IDL_NODE_STRUCT); + + ((GIdlNode *)struct_)->name = g_strdup (name); + if (deprecated && strcmp (deprecated, "1") == 0) + struct_->deprecated = TRUE; + else + struct_->deprecated = FALSE; + + ctx->current_node = (GIdlNode *)struct_; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, struct_); + + ctx->state = STATE_STRUCT; + } + return TRUE; + } + return FALSE; +} + + +static gboolean +start_union (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "union") == 0 && + ctx->state == STATE_NAMESPACE) + { + const gchar *name; + const gchar *deprecated; + const gchar *typename; + const gchar *typeinit; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + typename = find_attribute ("type-name", attribute_names, attribute_values); + typeinit = find_attribute ("get-type", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeUnion *union_; + + union_ = (GIdlNodeUnion *) g_idl_node_new (G_IDL_NODE_UNION); + + ((GIdlNode *)union_)->name = g_strdup (name); + union_->gtype_name = g_strdup (typename); + union_->gtype_init = g_strdup (typeinit); + if (deprecated && strcmp (deprecated, "1") == 0) + union_->deprecated = TRUE; + else + union_->deprecated = FALSE; + + ctx->current_node = (GIdlNode *)union_; + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, union_); + + ctx->state = STATE_UNION; + } + return TRUE; + } + return FALSE; +} + +static gboolean +start_discriminator (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + if (strcmp (element_name, "discriminator") == 0 && + ctx->state == STATE_UNION) + { + const gchar *type; + const gchar *offset; + + type = find_attribute ("type", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + if (type == NULL) + MISSING_ATTRIBUTE (error, element_name, "type"); + else if (offset == NULL) + MISSING_ATTRIBUTE (error, element_name, "offset"); + { + ((GIdlNodeUnion *)ctx->current_node)->discriminator_type + = parse_type (type); + ((GIdlNodeUnion *)ctx->current_node)->discriminator_offset + = atoi (offset); + } + + return TRUE; + } + + return FALSE; +} + +static void +start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + gint line_number, char_number; + + switch (element_name[0]) + { + case 'a': + if (strcmp (element_name, "api") == 0 && ctx->state == STATE_START) + { + const gchar *version; + + version = find_attribute ("version", attribute_names, attribute_values); + + if (version == NULL) + MISSING_ATTRIBUTE (error, element_name, "version"); + else if (strcmp (version, "1.0") != 0) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unsupported version '%s'", + version); + else + ctx->state = STATE_ROOT; + + goto out; + } + break; + + case 'b': + if (start_boxed (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'c': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_constant (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'd': + if (start_discriminator (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'e': + if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_errordomain (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'f': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_field (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + + break; + + case 'i': + if (start_interface (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + if (strcmp (element_name, "implements") == 0 && + ctx->state == STATE_OBJECT) + { + ctx->state = STATE_IMPLEMENTS; + + goto out; + } + else if (strcmp (element_name, "interface") == 0 && + ctx->state == STATE_IMPLEMENTS) + { + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface ->interfaces = g_list_append (iface->interfaces, g_strdup (name)); + } + + goto out; + } + else if (strcmp (element_name, "interface") == 0 && + ctx->state == STATE_REQUIRES) + { + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface ->prerequisites = g_list_append (iface->prerequisites, g_strdup (name)); + } + + goto out; + } + break; + + case 'm': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_member (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'n': + if (strcmp (element_name, "namespace") == 0 && ctx->state == STATE_ROOT) + { + const gchar *name, *shared_library; + + name = find_attribute ("name", attribute_names, attribute_values); + shared_library = find_attribute ("shared-library", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + ctx->current_module = g_idl_module_new (name, shared_library); + ctx->modules = g_list_append (ctx->modules, ctx->current_module); + + ctx->state = STATE_NAMESPACE; + } + + goto out; + } + break; + + case 'o': + if (start_object (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "object") == 0 && + ctx->state == STATE_REQUIRES) + { + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (error, element_name, "name"); + else + { + GIdlNodeInterface *iface; + + iface = (GIdlNodeInterface *)ctx->current_node; + iface ->prerequisites = g_list_append (iface->prerequisites, g_strdup (name)); + } + + goto out; + } + break; + + case 'p': + if (start_property (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "parameters") == 0 && + ctx->state == STATE_FUNCTION) + { + ctx->state = STATE_PARAMETERS; + + goto out; + } + else if (start_parameter (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + + break; + + case 'r': + if (start_return_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "requires") == 0 && + ctx->state == STATE_INTERFACE) + { + ctx->state = STATE_REQUIRES; + + goto out; + } + + break; + + case 's': + if (start_signal (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_struct (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + + break; + + case 'u': + if (start_union (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'v': + if (start_vfunc (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + } + + g_markup_parse_context_get_position (context, &line_number, &char_number); + + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ELEMENT, + "Unexpected start tag '%s' on line %d char %d", + element_name, + line_number, char_number); + + out: ; + +} + +static void +end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + switch (ctx->state) + { + case STATE_START: + case STATE_END: + /* no need to GError here, GMarkup already catches this */ + break; + + case STATE_ROOT: + ctx->state = STATE_END; + break; + + case STATE_NAMESPACE: + if (strcmp (element_name, "namespace") == 0) + { + ctx->current_module = NULL; + ctx->state = STATE_ROOT; + } + break; + + case STATE_FUNCTION: + if (strcmp (element_name, "return-type") == 0) + /* do nothing */ ; + + else if (ctx->current_node == g_list_last (ctx->current_module->entries)->data) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + else + { + ctx->current_node = g_list_last (ctx->current_module->entries)->data; + if (ctx->current_node->type == G_IDL_NODE_INTERFACE) + ctx->state = STATE_INTERFACE; + else if (ctx->current_node->type == G_IDL_NODE_OBJECT) + ctx->state = STATE_OBJECT; + else if (ctx->current_node->type == G_IDL_NODE_BOXED) + ctx->state = STATE_BOXED; + else if (ctx->current_node->type == G_IDL_NODE_STRUCT) + ctx->state = STATE_STRUCT; + else if (ctx->current_node->type == G_IDL_NODE_UNION) + ctx->state = STATE_UNION; + } + break; + + case STATE_OBJECT: + if (strcmp (element_name, "object") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_ERRORDOMAIN: + if (strcmp (element_name, "errordomain") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_INTERFACE: + if (strcmp (element_name, "interface") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_ENUM: + if (strcmp (element_name, "enum") == 0 || + strcmp (element_name, "flags") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_BOXED: + if (strcmp (element_name, "boxed") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_STRUCT: + if (strcmp (element_name, "struct") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + case STATE_UNION: + if (strcmp (element_name, "union") == 0) + { + ctx->current_node = NULL; + ctx->state = STATE_NAMESPACE; + } + break; + + case STATE_IMPLEMENTS: + if (strcmp (element_name, "implements") == 0) + ctx->state = STATE_OBJECT; + break; + case STATE_REQUIRES: + if (strcmp (element_name, "requires") == 0) + ctx->state = STATE_INTERFACE; + break; + case STATE_PARAMETERS: + if (strcmp (element_name, "parameters") == 0) + ctx->state = STATE_FUNCTION; + break; + default: + g_error ("Unhandled state %d in end_element_handler\n", ctx->state); + } +} + +static void +text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + /* FIXME warn about non-whitespace text */ +} + +static void +cleanup (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + ParseContext *ctx = user_data; + GList *m; + + for (m = ctx->modules; m; m = m->next) + g_idl_module_free (m->data); + g_list_free (ctx->modules); + ctx->modules = NULL; + + ctx->current_module = NULL; +} + +static GMarkupParser parser = +{ + start_element_handler, + end_element_handler, + text_handler, + NULL, + cleanup +}; + +GList * +g_idl_parse_string (const gchar *buffer, + gssize length, + GError **error) +{ + ParseContext ctx = { 0 }; + GMarkupParseContext *context; + + ctx.state = STATE_START; + + context = g_markup_parse_context_new (&parser, 0, &ctx, NULL); + if (!g_markup_parse_context_parse (context, buffer, length, error)) + goto out; + + if (!g_markup_parse_context_end_parse (context, error)) + goto out; + + out: + + g_markup_parse_context_free (context); + + return ctx.modules; +} + +GList * +g_idl_parse_file (const gchar *filename, + GError **error) +{ + gchar *buffer; + gsize length; + GList *modules; + + if (!g_file_get_contents (filename, &buffer, &length, error)) + return NULL; + + modules = g_idl_parse_string (buffer, length, error); + + g_free (buffer); + + return modules; +} + + diff --git a/tools/gidlparser.h b/tools/gidlparser.h new file mode 100644 index 00000000..2d71aaa0 --- /dev/null +++ b/tools/gidlparser.h @@ -0,0 +1,38 @@ +/* GObject introspection: A parser for the XML IDL format + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IDL_PARSER_H__ +#define __G_IDL_PARSER_H__ + +#include <glib.h> + +G_BEGIN_DECLS + + +GList *g_idl_parse_string (const gchar *buffer, + gssize length, + GError **error); +GList *g_idl_parse_file (const gchar *filename, + GError **error); + + +G_END_DECLS + +#endif /* __G_IDL_PARSER_H__ */ diff --git a/tools/gidlwriter.c b/tools/gidlwriter.c new file mode 100644 index 00000000..d46762bc --- /dev/null +++ b/tools/gidlwriter.c @@ -0,0 +1,483 @@ +/* GObject introspection: gen-introspect + * + * Copyright (C) 2007 Jürg Billeter + * Copyright (C) 2007 Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: + * Jürg Billeter <j@bitron.ch> + */ + +#include <stdio.h> +#include <glib.h> +#include "scanner.h" +#include "gidlnode.h" + +typedef struct { + int indent; + FILE *output; +} GIdlWriter; + +static void node_generate (GIdlWriter * writer, GIdlNode * node); + +static void +g_writer_write_inline (GIdlWriter * writer, const char *s) +{ + fprintf (writer->output, "%s", s); +} + +static void +g_writer_write (GIdlWriter * writer, const char *s) +{ + int i; + for (i = 0; i < writer->indent; i++) + { + fprintf (writer->output, "\t"); + } + + g_writer_write_inline (writer, s); +} + +static void +g_writer_write_indent (GIdlWriter * writer, const char *s) +{ + g_writer_write (writer, s); + writer->indent++; +} + +static void +g_writer_write_unindent (GIdlWriter * writer, const char *s) +{ + writer->indent--; + g_writer_write (writer, s); +} + +static void +field_generate (GIdlWriter * writer, GIdlNodeField * node) +{ + char *markup = + g_markup_printf_escaped ("<field name=\"%s\" type=\"%s\"/>\n", + node->node.name, node->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); +} + +static void +value_generate (GIdlWriter * writer, GIdlNodeValue * node) +{ + char *markup = + g_markup_printf_escaped ("<member name=\"%s\" value=\"%d\"/>\n", + node->node.name, node->value); + g_writer_write (writer, markup); + g_free (markup); +} + +static void +constant_generate (GIdlWriter * writer, GIdlNodeConstant * node) +{ + char *markup = + g_markup_printf_escaped + ("<constant name=\"%s\" type=\"%s\" value=\"%s\"/>\n", node->node.name, + node->type->unparsed, node->value); + g_writer_write (writer, markup); + g_free (markup); +} + +static void +property_generate (GIdlWriter * writer, GIdlNodeProperty * node) +{ + char *markup = + g_markup_printf_escaped ("<property name=\"%s\" " + "type=\"%s\" " + "readable=\"%s\" " + "writable=\"%s\" " + "construct=\"%s\" " + "construct-only=\"%s\"/>\n", + node->node.name, + node->type->unparsed, + node->readable ? "1" : "0", + node->writable ? "1" : "0", + node->construct ? "1" : "0", + node->construct_only ? "1" : "0"); + g_writer_write (writer, markup); + g_free (markup); +} + +static void +function_generate (GIdlWriter * writer, GIdlNodeFunction * node) +{ + const char *tag_name; + GString *markup_s; + gchar *markup; + + if (node->node.type == G_IDL_NODE_CALLBACK) + tag_name = "callback"; + else if (node->is_constructor) + tag_name = "constructor"; + else if (node->is_method) + tag_name = "method"; + else + tag_name = "function"; + + markup_s = g_string_new ("<"); + g_string_append_printf (markup_s, + "%s name=\"%s\"", + tag_name, node->node.name); + + if (node->node.type != G_IDL_NODE_CALLBACK) + g_string_append_printf (markup_s, + g_markup_printf_escaped (" symbol=\"%s\"", node->symbol)); + + if (node->deprecated) + g_string_append_printf (markup_s, " deprecated=\"1\""); + + g_string_append (markup_s, ">\n"); + + g_writer_write_indent (writer, markup_s->str); + g_string_free (markup_s, TRUE); + + markup = + g_markup_printf_escaped ("<return-type type=\"%s\"/>\n", + node->result->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + + if (node->parameters != NULL) + { + GList *l; + g_writer_write_indent (writer, "<parameters>\n"); + for (l = node->parameters; l != NULL; l = l->next) + { + GIdlNodeParam *param = l->data; + markup = + g_markup_printf_escaped ("<parameter name=\"%s\" type=\"%s\"/>\n", + param->node.name, param->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + } + g_writer_write_unindent (writer, "</parameters>\n"); + } + markup = g_strdup_printf ("</%s>\n", tag_name); + g_writer_write_unindent (writer, markup); + g_free (markup); +} + +static void +vfunc_generate (GIdlWriter * writer, GIdlNodeVFunc * node) +{ + char *markup = + g_markup_printf_escaped ("<vfunc name=\"%s\">\n", node->node.name); + g_writer_write_indent (writer, markup); + g_free (markup); + markup = + g_markup_printf_escaped ("<return-type type=\"%s\"/>\n", + node->result->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + if (node->parameters != NULL) + { + GList *l; + g_writer_write_indent (writer, "<parameters>\n"); + for (l = node->parameters; l != NULL; l = l->next) + { + GIdlNodeParam *param = l->data; + markup = + g_markup_printf_escaped ("<parameter name=\"%s\" type=\"%s\"/>\n", + param->node.name, param->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + } + g_writer_write_unindent (writer, "</parameters>\n"); + } + g_writer_write_unindent (writer, "</vfunc>\n"); +} + +static void +signal_generate (GIdlWriter * writer, GIdlNodeSignal * node) +{ + char *markup; + const char *when = "LAST"; + if (node->run_first) + { + when = "FIRST"; + } + else if (node->run_cleanup) + { + when = "CLEANUP"; + } + markup = + g_markup_printf_escaped ("<signal name=\"%s\" when=\"%s\">\n", + node->node.name, when); + g_writer_write_indent (writer, markup); + g_free (markup); + markup = + g_markup_printf_escaped ("<return-type type=\"%s\"/>\n", + node->result->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + if (node->parameters != NULL) + { + GList *l; + g_writer_write_indent (writer, "<parameters>\n"); + for (l = node->parameters; l != NULL; l = l->next) + { + GIdlNodeParam *param = l->data; + markup = + g_markup_printf_escaped ("<parameter name=\"%s\" type=\"%s\"/>\n", + param->node.name, param->type->unparsed); + g_writer_write (writer, markup); + g_free (markup); + } + g_writer_write_unindent (writer, "</parameters>\n"); + } + g_writer_write_unindent (writer, "</signal>\n"); +} + +static void +interface_generate (GIdlWriter * writer, GIdlNodeInterface * node) +{ + GList *l; + char *markup; + if (node->node.type == G_IDL_NODE_OBJECT) + { + markup = + g_markup_printf_escaped ("<object name=\"%s\" " + "parent=\"%s\" " + "type-name=\"%s\" " + "get-type=\"%s\">\n", + node->node.name, + node->parent, + node->gtype_name, + node->gtype_init); + } + else if (node->node.type == G_IDL_NODE_INTERFACE) + { + markup = + g_markup_printf_escaped + ("<interface name=\"%s\" type-name=\"%s\" get-type=\"%s\">\n", + node->node.name, node->gtype_name, node->gtype_init); + } + + g_writer_write_indent (writer, markup); + g_free (markup); + if (node->node.type == G_IDL_NODE_OBJECT && node->interfaces != NULL) + { + GList *l; + g_writer_write_indent (writer, "<implements>\n"); + for (l = node->interfaces; l != NULL; l = l->next) + { + markup = + g_markup_printf_escaped ("<interface name=\"%s\"/>\n", + (char *) l->data); + g_writer_write (writer, markup); + g_free (markup); + } + g_writer_write_unindent (writer, "</implements>\n"); + } + else if (node->node.type == G_IDL_NODE_INTERFACE + && node->prerequisites != NULL) + { + GList *l; + g_writer_write_indent (writer, "<requires>\n"); + for (l = node->prerequisites; l != NULL; l = l->next) + { + markup = + g_markup_printf_escaped ("<interface name=\"%s\"/>\n", + (char *) l->data); + g_writer_write (writer, markup); + g_free (markup); + } + g_writer_write_unindent (writer, "</requires>\n"); + } + + for (l = node->members; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + + if (node->node.type == G_IDL_NODE_OBJECT) + { + g_writer_write_unindent (writer, "</object>\n"); + } + else if (node->node.type == G_IDL_NODE_INTERFACE) + { + g_writer_write_unindent (writer, "</interface>\n"); + } +} + +static void +struct_generate (GIdlWriter * writer, GIdlNodeStruct * node) +{ + GList *l; + char *markup = + g_markup_printf_escaped ("<struct name=\"%s\">\n", node->node.name); + g_writer_write_indent (writer, markup); + g_free (markup); + for (l = node->members; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + g_writer_write_unindent (writer, "</struct>\n"); +} + +static void +union_generate (GIdlWriter * writer, GIdlNodeUnion * node) +{ + GList *l; + char *markup = + g_markup_printf_escaped ("<union name=\"%s\">\n", node->node.name); + g_writer_write_indent (writer, markup); + g_free (markup); + for (l = node->members; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + g_writer_write_unindent (writer, "</union>\n"); +} + +static void +boxed_generate (GIdlWriter * writer, GIdlNodeBoxed * node) +{ + GList *l; + char *markup = + g_markup_printf_escaped + ("<boxed name=\"%s\" type-name=\"%s\" get-type=\"%s\">\n", + node->node.name, node->gtype_name, node->gtype_init); + g_writer_write_indent (writer, markup); + g_free (markup); + for (l = node->members; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + g_writer_write_unindent (writer, "</boxed>\n"); +} + +static void +enum_generate (GIdlWriter * writer, GIdlNodeEnum * node) +{ + GList *l; + char *markup; + const char *tag_name = NULL; + + if (node->node.type == G_IDL_NODE_ENUM) + { + tag_name = "enum"; + } + else if (node->node.type == G_IDL_NODE_FLAGS) + { + tag_name = "flags"; + } + markup = + g_markup_printf_escaped ("<%s name=\"%s\">\n", tag_name, node->node.name); + g_writer_write_indent (writer, markup); + g_free (markup); + + for (l = node->values; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + + markup = g_strdup_printf ("</%s>\n", tag_name); + g_writer_write_unindent (writer, markup); + g_free (markup); +} + +static void +node_generate (GIdlWriter * writer, GIdlNode * node) +{ + switch (node->type) + { + case G_IDL_NODE_FUNCTION: + case G_IDL_NODE_CALLBACK: + function_generate (writer, (GIdlNodeFunction *) node); + break; + case G_IDL_NODE_VFUNC: + vfunc_generate (writer, (GIdlNodeVFunc *) node); + break; + case G_IDL_NODE_OBJECT: + case G_IDL_NODE_INTERFACE: + interface_generate (writer, (GIdlNodeInterface *) node); + break; + case G_IDL_NODE_STRUCT: + struct_generate (writer, (GIdlNodeStruct *) node); + break; + case G_IDL_NODE_UNION: + union_generate (writer, (GIdlNodeUnion *) node); + break; + case G_IDL_NODE_BOXED: + boxed_generate (writer, (GIdlNodeBoxed *) node); + break; + case G_IDL_NODE_ENUM: + case G_IDL_NODE_FLAGS: + enum_generate (writer, (GIdlNodeEnum *) node); + break; + case G_IDL_NODE_PROPERTY: + property_generate (writer, (GIdlNodeProperty *) node); + break; + case G_IDL_NODE_FIELD: + field_generate (writer, (GIdlNodeField *) node); + break; + case G_IDL_NODE_SIGNAL: + signal_generate (writer, (GIdlNodeSignal *) node); + break; + case G_IDL_NODE_VALUE: + value_generate (writer, (GIdlNodeValue *) node); + break; + case G_IDL_NODE_CONSTANT: + constant_generate (writer, (GIdlNodeConstant *) node); + break; + default: + g_assert_not_reached (); + } +} + +static void +g_writer_write_module (GIdlWriter * writer, GIdlModule * module) +{ + GList *l; + char *markup = + g_markup_printf_escaped ("<namespace name=\"%s\">\n", module->name); + g_writer_write_indent (writer, markup); + g_free (markup); + for (l = module->entries; l != NULL; l = l->next) + { + node_generate (writer, l->data); + } + g_writer_write_unindent (writer, "</namespace>\n"); +} + +void +g_idl_writer_save_file (GIdlModule *module, + const gchar *filename) +{ + GIdlWriter *writer; + + writer = g_new0 (GIdlWriter, 1); + + if (!filename) + writer->output = stdout; + else + writer->output = fopen (filename, "w"); + + g_writer_write (writer, "<?xml version=\"1.0\"?>\n"); + g_writer_write_indent (writer, "<api version=\"1.0\">\n"); + g_writer_write_module (writer, module); + g_writer_write_unindent (writer, "</api>\n"); + + if (filename) + fclose (writer->output); +} diff --git a/tools/gidlwriter.h b/tools/gidlwriter.h new file mode 100644 index 00000000..5d41a0c6 --- /dev/null +++ b/tools/gidlwriter.h @@ -0,0 +1,26 @@ +/* GObject introspection: IDL writer + * + * Copyright (C) 2007 Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IDL_WRITER_H__ +#define __G_IDL_WRITER_H__ + +void g_idl_writer_save_file (GIdlModule *module, const gchar *filename); + +#endif /* __G_IDL_WRITER_H__ */ diff --git a/tools/scanner.c b/tools/scanner.c new file mode 100644 index 00000000..abbe2156 --- /dev/null +++ b/tools/scanner.c @@ -0,0 +1,1762 @@ +/* GObject introspection: scanner + * + * Copyright (C) 2007-2008 Jürg Billeter + * Copyright (C) 2007 Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: + * Jürg Billeter <j@bitron.ch> + */ + +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <sys/wait.h> /* waitpid */ +#include <gmodule.h> +#include "scanner.h" +#include "gidlparser.h" +#include "gidlmodule.h" +#include "gidlnode.h" +#include "gidlwriter.h" + +typedef GType (*TypeFunction) (void); + +static void g_igenerator_parse_macros (GIGenerator * igenerator); + +static GIGenerator * +g_igenerator_new (const gchar *namespace, + const gchar *shared_library) +{ + GIGenerator *igenerator = g_new0 (GIGenerator, 1); + igenerator->namespace = g_strdup (namespace); + igenerator->shared_library = g_strdup (shared_library); + igenerator->lower_case_namespace = + g_ascii_strdown (igenerator->namespace, -1); + igenerator->module = g_idl_module_new (namespace, shared_library); + + igenerator->typedef_table = g_hash_table_new (g_str_hash, g_str_equal); + igenerator->struct_or_union_or_enum_table = + g_hash_table_new (g_str_hash, g_str_equal); + + igenerator->type_map = g_hash_table_new (g_str_hash, g_str_equal); + igenerator->type_by_lower_case_prefix = + g_hash_table_new (g_str_hash, g_str_equal); + igenerator->symbols = g_hash_table_new (g_str_hash, g_str_equal); + + return igenerator; +} + +static void +g_igenerator_free (GIGenerator *generator) +{ + g_free (generator->namespace); + g_free (generator->shared_library); + g_free (generator->lower_case_namespace); +#if 0 + g_idl_module_free (generator->module); +#endif + g_hash_table_destroy (generator->typedef_table); + g_hash_table_destroy (generator->struct_or_union_or_enum_table); + g_hash_table_destroy (generator->type_map); + g_hash_table_destroy (generator->type_by_lower_case_prefix); + g_hash_table_destroy (generator->symbols); + g_list_foreach (generator->filenames, (GFunc)g_free, NULL); + g_list_free (generator->filenames); +#if 0 + g_list_foreach (generator->symbol_list, (GFunc)csymbol_free, NULL); + g_list_free (generator->symbol_list); +#endif + g_free (generator); +} + +static GIdlNodeType * +create_node_from_gtype (GType type_id) +{ + GIdlNodeType *node; + GType fundamental; + + node = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE); + + fundamental = g_type_fundamental (type_id); + switch (fundamental) + { + case G_TYPE_STRING: + node->unparsed = g_strdup ("char*"); + break; + case G_TYPE_INTERFACE: + case G_TYPE_BOXED: + case G_TYPE_OBJECT: + node->unparsed = g_strdup_printf ("%s*", g_type_name (type_id)); + break; + case G_TYPE_PARAM: + node->unparsed = g_strdup ("GParamSpec*"); + break; + default: + if (fundamental == G_TYPE_STRV) + node->unparsed = g_strdup ("char*[]"); + else + node->unparsed = g_strdup (g_type_name (type_id)); + break; + } + + return node; +} + +static GIdlNodeType * +create_node_from_ctype (CType * ctype) +{ + GIdlNodeType *node; + + node = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE); + + switch (ctype->type) + { + case CTYPE_VOID: + node->unparsed = g_strdup ("void"); + break; + case CTYPE_BASIC_TYPE: + node->unparsed = g_strdup (ctype->name); + break; + case CTYPE_TYPEDEF: + node->unparsed = g_strdup (ctype->name); + break; + case CTYPE_STRUCT: + if (ctype->name == NULL) + /* anonymous struct */ + node->unparsed = g_strdup ("gpointer"); + else + node->unparsed = g_strdup_printf ("struct %s", ctype->name); + break; + case CTYPE_UNION: + if (ctype->name == NULL) + /* anonymous union */ + node->unparsed = g_strdup ("gpointer"); + else + node->unparsed = g_strdup_printf ("union %s", ctype->name); + break; + case CTYPE_ENUM: + if (ctype->name == NULL) + /* anonymous enum */ + node->unparsed = g_strdup ("gint"); + else + node->unparsed = g_strdup_printf ("enum %s", ctype->name); + break; + case CTYPE_POINTER: + if (ctype->base_type->type == CTYPE_FUNCTION) + /* anonymous function pointer */ + node->unparsed = g_strdup ("GCallback"); + else + { + GIdlNodeType *gibasetype = create_node_from_ctype (ctype->base_type); + node->unparsed = g_strdup_printf ("%s*", gibasetype->unparsed); + } + break; + case CTYPE_ARRAY: + { + GIdlNodeType *gibasetype = create_node_from_ctype (ctype->base_type); + node->unparsed = g_strdup_printf ("%s[]", gibasetype->unparsed); + break; + } + default: + node->unparsed = g_strdup ("unknown"); + break; + } + + return node; +} + +static char * +str_replace (const char *str, const char *needle, const char *replacement) +{ + char **strings = g_strsplit (str, needle, 0); + char *result = g_strjoinv (replacement, strings); + g_strfreev (strings); + return result; +} + +static void +g_igenerator_process_properties (GIGenerator * igenerator, + GIdlNodeInterface * node, GType type_id) +{ + int i; + guint n_properties; + GParamSpec **properties; + + if (node->node.type == G_IDL_NODE_OBJECT) + { + GObjectClass *type_class = g_type_class_ref (type_id); + properties = g_object_class_list_properties (type_class, &n_properties); + } + else if (node->node.type == G_IDL_NODE_INTERFACE) + { + GTypeInterface *iface = g_type_default_interface_ref (type_id); + properties = g_object_interface_list_properties (iface, &n_properties); + } + else + { + g_assert_not_reached (); + } + + for (i = 0; i < n_properties; i++) + { + GIdlNodeProperty *giprop; + + /* ignore inherited properties */ + if (properties[i]->owner_type != type_id) + { + continue; + } + giprop = (GIdlNodeProperty *) g_idl_node_new (G_IDL_NODE_PROPERTY); + giprop->node.name = properties[i]->name; + node->members = + g_list_insert_sorted (node->members, giprop, + (GCompareFunc) g_idl_node_cmp); + giprop->type = create_node_from_gtype (properties[i]->value_type); + giprop->readable = (properties[i]->flags & G_PARAM_READABLE) != 0; + giprop->writable = (properties[i]->flags & G_PARAM_WRITABLE) != 0; + giprop->construct = (properties[i]->flags & G_PARAM_CONSTRUCT) != 0; + giprop->construct_only = + (properties[i]->flags & G_PARAM_CONSTRUCT_ONLY) != 0; + } +} + +static void +g_igenerator_process_signals (GIGenerator * igenerator, + GIdlNodeInterface * node, GType type_id) +{ + int i, j; + guint n_signal_ids; + guint *signal_ids = g_signal_list_ids (type_id, &n_signal_ids); + + for (i = 0; i < n_signal_ids; i++) + { + GSignalQuery signal_query; + GIdlNodeSignal *gisig; + GIdlNodeParam *giparam; + + g_signal_query (signal_ids[i], &signal_query); + gisig = (GIdlNodeSignal *) g_idl_node_new (G_IDL_NODE_SIGNAL); + gisig->node.name = g_strdup (signal_query.signal_name); + node->members = + g_list_insert_sorted (node->members, gisig, + (GCompareFunc) g_idl_node_cmp); + + gisig->run_first = + (signal_query.signal_flags & G_SIGNAL_RUN_FIRST) != 0; + gisig->run_last = (signal_query.signal_flags & G_SIGNAL_RUN_LAST) != 0; + gisig->run_cleanup = + (signal_query.signal_flags & G_SIGNAL_RUN_CLEANUP) != 0; + + /* add sender parameter */ + giparam = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + gisig->parameters = g_list_append (gisig->parameters, giparam); + giparam->node.name = g_strdup ("object"); + giparam->type = create_node_from_gtype (type_id); + + for (j = 0; j < signal_query.n_params; j++) + { + giparam = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + gisig->parameters = g_list_append (gisig->parameters, giparam); + giparam->node.name = g_strdup_printf ("p%d", j); + giparam->type = create_node_from_gtype (signal_query.param_types[j]); + } + gisig->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + gisig->result->type = create_node_from_gtype (signal_query.return_type); + } +} + +static const gchar * +lookup_symbol (GIGenerator *igenerator, const gchar *typename) +{ + const gchar *name = + g_hash_table_lookup (igenerator->symbols, typename); + + if (!name) + { + g_printerr ("Unknown symbol: %s\n", typename); + return typename; + } + + return name; +} + +static void +g_igenerator_create_object (GIGenerator *igenerator, + const char *symbol_name, + GType type_id, + char *lower_case_prefix) + +{ + char *alt_lower_case_prefix; + GIdlNodeInterface *node; + guint n_type_interfaces; + GType *type_interfaces; + int i; + + node = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_OBJECT); + node->node.name = g_strdup (g_type_name (type_id)); + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + g_hash_table_insert (igenerator->type_map, node->node.name, + node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + alt_lower_case_prefix = g_ascii_strdown (node->node.name, -1); + + if (strcmp (alt_lower_case_prefix, lower_case_prefix) != 0) + { + /* alternative prefix sometimes necessary, for example + * for GdkWindow + */ + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + alt_lower_case_prefix, node); + } + else + { + g_free (alt_lower_case_prefix); + } + + node->gtype_name = node->node.name; + node->gtype_init = g_strdup (symbol_name); + node->parent = g_strdup (lookup_symbol (igenerator, + g_type_name (g_type_parent (type_id)))); + + type_interfaces = g_type_interfaces (type_id, &n_type_interfaces); + for (i = 0; i < n_type_interfaces; i++) + { + char *iface_name = + g_strdup (g_type_name (type_interfaces[i])); + /* workaround for AtkImplementorIface */ + if (g_str_has_suffix (iface_name, "Iface")) + { + iface_name[strlen (iface_name) - strlen ("Iface")] = + '\0'; + } + node->interfaces = + g_list_append (node->interfaces, iface_name); + } + + g_hash_table_insert (igenerator->symbols, + g_strdup (node->gtype_name), + /* FIXME: Strip igenerator->namespace */ + g_strdup (node->node.name)); + + g_igenerator_process_properties (igenerator, node, type_id); + g_igenerator_process_signals (igenerator, node, type_id); +} + +static void +g_igenerator_create_interface (GIGenerator *igenerator, + const char *symbol_name, + GType type_id, + char *lower_case_prefix) + +{ + GIdlNodeInterface *node; + gboolean is_gobject = FALSE; + guint n_iface_prereqs; + GType *iface_prereqs; + int i; + + node = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_INTERFACE); + node->node.name = g_strdup (g_type_name (type_id)); + + /* workaround for AtkImplementorIface */ + if (g_str_has_suffix (node->node.name, "Iface")) + { + node->node.name[strlen (node->node.name) - + strlen ("Iface")] = '\0'; + } + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + g_hash_table_insert (igenerator->type_map, node->node.name, + node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + node->gtype_name = node->node.name; + node->gtype_init = g_strdup (symbol_name); + + iface_prereqs = + g_type_interface_prerequisites (type_id, &n_iface_prereqs); + + for (i = 0; i < n_iface_prereqs; i++) + { + if (g_type_fundamental (iface_prereqs[i]) == G_TYPE_OBJECT) + { + is_gobject = TRUE; + } + node->prerequisites = + g_list_append (node->prerequisites, + g_strdup (g_type_name (iface_prereqs[i]))); + } + + if (is_gobject) + g_igenerator_process_properties (igenerator, node, type_id); + else + g_type_default_interface_ref (type_id); + + g_igenerator_process_signals (igenerator, node, type_id); +} + +static void +g_igenerator_create_boxed (GIGenerator *igenerator, + const char *symbol_name, + GType type_id, + char *lower_case_prefix) +{ + GIdlNodeBoxed *node = + (GIdlNodeBoxed *) g_idl_node_new (G_IDL_NODE_BOXED); + node->node.name = g_strdup (g_type_name (type_id)); + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + g_hash_table_insert (igenerator->type_map, node->node.name, + node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + node->gtype_name = node->node.name; + node->gtype_init = g_strdup (symbol_name); +} + +static void +g_igenerator_create_enum (GIGenerator *igenerator, + const char *symbol_name, + GType type_id, + char *lower_case_prefix) +{ + GIdlNodeEnum *node; + int i; + GEnumClass *type_class; + + node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_ENUM); + node->node.name = g_strdup (g_type_name (type_id)); + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + g_hash_table_insert (igenerator->type_map, node->node.name, + node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + node->gtype_name = node->node.name; + node->gtype_init = g_strdup (symbol_name); + + type_class = g_type_class_ref (type_id); + + for (i = 0; i < type_class->n_values; i++) + { + GIdlNodeValue *gival = + (GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE); + node->values = g_list_append (node->values, gival); + gival->node.name = + g_strdup (type_class->values[i].value_name); + gival->value = type_class->values[i].value; + } +} + +static void +g_igenerator_create_flags (GIGenerator *igenerator, + const char *symbol_name, + GType type_id, + char *lower_case_prefix) +{ + GIdlNodeEnum *node; + GFlagsClass *type_class; + int i; + + node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_FLAGS); + node->node.name = g_strdup (g_type_name (type_id)); + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + g_hash_table_insert (igenerator->type_map, node->node.name, + node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + node->gtype_name = node->node.name; + node->gtype_init = g_strdup (symbol_name); + + type_class = g_type_class_ref (type_id); + + for (i = 0; i < type_class->n_values; i++) + { + GIdlNodeValue *gival = + (GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE); + node->values = g_list_append (node->values, gival); + gival->node.name = + g_strdup (type_class->values[i].value_name); + gival->value = type_class->values[i].value; + } +} + +static gboolean +g_igenerator_process_module_symbol (GIGenerator *igenerator, + GModule *module, + const gchar *symbol_name) +{ + TypeFunction type_fun; + GType type_id; + GType type_fundamental; + char *lower_case_prefix; + + /* ignore already processed functions */ + if (symbol_name == NULL) + return FALSE; + + if (!g_module_symbol (module, + symbol_name, + (gpointer *) & type_fun)) + return FALSE; + + type_id = type_fun (); + type_fundamental = g_type_fundamental (type_id); + lower_case_prefix = + str_replace (g_strndup + (symbol_name, + strlen (symbol_name) - strlen ("_get_type")), + "_", ""); + + switch (type_fundamental) + { + case G_TYPE_OBJECT: + g_igenerator_create_object (igenerator, symbol_name, type_id, + lower_case_prefix); + break; + case G_TYPE_INTERFACE: + g_igenerator_create_interface (igenerator, symbol_name, type_id, + lower_case_prefix); + break; + case G_TYPE_BOXED: + g_igenerator_create_boxed (igenerator, symbol_name, type_id, + lower_case_prefix); + break; + case G_TYPE_ENUM: + g_igenerator_create_enum (igenerator, symbol_name, type_id, + lower_case_prefix); + break; + case G_TYPE_FLAGS: + g_igenerator_create_flags (igenerator, symbol_name, type_id, + lower_case_prefix); + break; + default: + break; + } + return TRUE; +} + +static void +g_igenerator_process_module (GIGenerator * igenerator, + const gchar *filename) +{ + GModule *module; + GList *l; + + module = g_module_open (filename, + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + + if (module == NULL) + { + g_critical ("Couldn't open module: %s", filename); + return; + } + + for (l = igenerator->get_type_symbols; l != NULL; l = l->next) + { + if (g_igenerator_process_module_symbol (igenerator, + module, (const char *)l->data)) + /* symbol found, ignore in future iterations */ + l->data = NULL; + } +} + +static void +g_igenerator_process_function_symbol (GIGenerator * igenerator, CSymbol * sym) +{ + GIdlNodeFunction *func; + char *last_underscore; + GList *param_l; + int i; + GSList *l; + + func = (GIdlNodeFunction *) g_idl_node_new (G_IDL_NODE_FUNCTION); + + /* check whether this is a type method */ + last_underscore = strrchr (sym->ident, '_'); + + while (last_underscore != NULL) + { + char *prefix; + GIdlNode *node; + + prefix = g_strndup (sym->ident, last_underscore - sym->ident); + prefix = str_replace (prefix, "_", ""); + + node = g_hash_table_lookup (igenerator->type_by_lower_case_prefix, + prefix); + if (node != NULL) + { + func->node.name = g_strdup (last_underscore + 1); + + /* ignore get_type functions in registered types */ + if (strcmp (func->node.name, "get_type") == 0) + return; + + if ((node->type == G_IDL_NODE_OBJECT || + node->type == G_IDL_NODE_BOXED) && + g_str_has_prefix (func->node.name, "new")) + func->is_constructor = TRUE; + else + func->is_method = TRUE; + + g_idl_node_add_member (node, func); + break; + } + else if (strcmp (igenerator->lower_case_namespace, prefix) == 0) + { + func->node.name = g_strdup (last_underscore + 1); + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, func, + (GCompareFunc) g_idl_node_cmp); + break; + } + last_underscore = + g_utf8_strrchr (sym->ident, last_underscore - sym->ident, '_'); + } + + /* create a namespace function if no prefix matches */ + if (func->node.name == NULL) + { + func->node.name = sym->ident; + func->is_constructor = FALSE; + func->is_method = FALSE; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, func, + (GCompareFunc) g_idl_node_cmp); + } + + func->symbol = sym->ident; + func->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + func->result->type = create_node_from_ctype (sym->base_type->base_type); + + for (param_l = sym->base_type->child_list, i = 1; param_l != NULL; + param_l = param_l->next, i++) + { + CSymbol *param_sym = param_l->data; + GIdlNodeParam *param; + + param = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + param->type = create_node_from_ctype (param_sym->base_type); + + if (param_sym->ident == NULL) + param->node.name = g_strdup_printf ("p%d", i); + else + param->node.name = param_sym->ident; + + func->parameters = g_list_append (func->parameters, param); + } + + for (l = sym->directives; l; l = l->next) + { + CDirective *directive = (CDirective*)l->data; + + if (!strcmp (directive->name, "deprecated")) + func->deprecated = TRUE; + else + g_printerr ("Unknown function directive: %s\n", + directive->name); + } +} + +static void +g_igenerator_process_unregistered_struct_typedef (GIGenerator * igenerator, + CSymbol * sym, + CType * struct_type) +{ + GIdlNodeStruct *node = + (GIdlNodeStruct *) g_idl_node_new (G_IDL_NODE_STRUCT); + GList *member_l; + char *lower_case_prefix; + + node->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + lower_case_prefix = g_ascii_strdown (sym->ident, -1); + g_hash_table_insert (igenerator->type_map, sym->ident, node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + + for (member_l = struct_type->child_list; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + GIdlNodeField *gifield = + (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD); + + node->members = g_list_append (node->members, gifield); + gifield->node.name = member->ident; + gifield->type = create_node_from_ctype (member->base_type); + } +} + +static void +g_igenerator_process_struct_typedef (GIGenerator * igenerator, CSymbol * sym) +{ + CType *struct_type = sym->base_type; + gboolean opaque_type = FALSE; + GIdlNode *type; + + if (struct_type->child_list == NULL) + { + CSymbol *struct_symbol; + g_assert (struct_type->name != NULL); + struct_symbol = + g_hash_table_lookup (igenerator->struct_or_union_or_enum_table, + struct_type->name); + if (struct_symbol != NULL) + { + struct_type = struct_symbol->base_type; + } + } + + if (struct_type->child_list == NULL) + { + opaque_type = TRUE; + } + + type = g_hash_table_lookup (igenerator->type_map, sym->ident); + if (type != NULL) + { + /* struct of a GTypeInstance */ + if (!opaque_type + && (type->type == G_IDL_NODE_OBJECT + || type->type == G_IDL_NODE_INTERFACE)) + { + GIdlNodeInterface *node = (GIdlNodeInterface *) type; + GList *member_l; + /* ignore first field => parent */ + for (member_l = struct_type->child_list->next; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + /* ignore private / reserved members */ + if (member->ident[0] == '_' + || g_str_has_prefix (member->ident, "priv")) + { + continue; + } + GIdlNodeField *gifield = + (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD); + node->members = g_list_append (node->members, gifield); + gifield->node.name = member->ident; + gifield->type = create_node_from_ctype (member->base_type); + } + } + else if (type->type == G_IDL_NODE_BOXED) + { + GIdlNodeBoxed *node = (GIdlNodeBoxed *) type; + GList *member_l; + for (member_l = struct_type->child_list; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + GIdlNodeField *gifield = + (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD); + node->members = g_list_append (node->members, gifield); + gifield->node.name = member->ident; + gifield->type = create_node_from_ctype (member->base_type); + } + } + } + else if (!opaque_type + && (g_str_has_suffix (sym->ident, "Class") + || g_str_has_suffix (sym->ident, "Iface") + || g_str_has_suffix (sym->ident, "Interface"))) + { + char *base_name; + GList *member_l; + GIdlNodeInterface *node; + + if (g_str_has_suffix (sym->ident, "Interface")) + { + base_name = + g_strndup (sym->ident, + strlen (sym->ident) - strlen ("Interface")); + } + else + { + base_name = + g_strndup (sym->ident, strlen (sym->ident) - strlen ("Class")); + } + type = g_hash_table_lookup (igenerator->type_map, base_name); + if (type == NULL + || (type->type != G_IDL_NODE_OBJECT + && type->type != G_IDL_NODE_INTERFACE)) + { + g_igenerator_process_unregistered_struct_typedef (igenerator, sym, + struct_type); + return; + } + node = (GIdlNodeInterface *) type; + + /* ignore first field => parent */ + for (member_l = struct_type->child_list->next; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + /* ignore private / reserved members */ + if (member->ident[0] == '_') + { + continue; + } + if (member->base_type->type == CTYPE_POINTER + && member->base_type->base_type->type == CTYPE_FUNCTION) + { + /* ignore default handlers of signals */ + gboolean found_signal = FALSE; + GList *type_member_l; + GList *param_l; + int i; + GIdlNodeVFunc *givfunc; + + for (type_member_l = node->members; type_member_l != NULL; + type_member_l = type_member_l->next) + { + GIdlNode *type_member = type_member_l->data; + char *normalized_name = + str_replace (type_member->name, "-", "_"); + if (type_member->type == G_IDL_NODE_SIGNAL + && strcmp (normalized_name, member->ident) == 0) + { + GList *vfunc_param_l; + GList *sig_param_l; + GIdlNodeSignal *sig = (GIdlNodeSignal *) type_member; + found_signal = TRUE; + /* set signal parameter names */ + for (vfunc_param_l = + member->base_type->base_type->child_list, + sig_param_l = sig->parameters; + vfunc_param_l != NULL && sig_param_l != NULL; + vfunc_param_l = vfunc_param_l->next, sig_param_l = + sig_param_l->next) + { + CSymbol *vfunc_param = vfunc_param_l->data; + GIdlNodeParam *sig_param = sig_param_l->data; + if (vfunc_param->ident != NULL) + { + g_free (sig_param->node.name); + sig_param->node.name = + g_strdup (vfunc_param->ident); + } + } + break; + } + } + if (found_signal) + { + continue; + } + + givfunc = (GIdlNodeVFunc *) g_idl_node_new (G_IDL_NODE_VFUNC); + givfunc->node.name = member->ident; + node->members = + g_list_insert_sorted (node->members, givfunc, + (GCompareFunc) g_idl_node_cmp); + givfunc->result = + (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + givfunc->result->type = + create_node_from_ctype (member->base_type->base_type->base_type); + for (param_l = member->base_type->base_type->child_list, i = 1; + param_l != NULL; param_l = param_l->next, i++) + { + CSymbol *param_sym = param_l->data; + GIdlNodeParam *param = + (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + if (param_sym->ident == NULL) + { + param->node.name = g_strdup_printf ("p%d", i); + } + else + { + param->node.name = param_sym->ident; + } + param->type = create_node_from_ctype (param_sym->base_type); + givfunc->parameters = + g_list_append (givfunc->parameters, param); + } + } + } + } + else if (g_str_has_suffix (sym->ident, "Private")) + { + /* ignore private structs */ + } + else + { + g_igenerator_process_unregistered_struct_typedef (igenerator, sym, + struct_type); + } +} + +static void +g_igenerator_process_union_typedef (GIGenerator * igenerator, CSymbol * sym) +{ + CType *union_type = sym->base_type; + gboolean opaque_type = FALSE; + GIdlNode *type; + + if (union_type->child_list == NULL) + { + g_assert (union_type->name != NULL); + CSymbol *union_symbol = + g_hash_table_lookup (igenerator->struct_or_union_or_enum_table, + union_type->name); + if (union_symbol != NULL) + { + union_type = union_symbol->base_type; + } + } + if (union_type->child_list == NULL) + { + opaque_type = TRUE; + } + + type = g_hash_table_lookup (igenerator->type_map, sym->ident); + if (type != NULL) + { + g_assert (type->type == G_IDL_NODE_BOXED); + GIdlNodeBoxed *node = (GIdlNodeBoxed *) type; + GList *member_l; + for (member_l = union_type->child_list; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + GIdlNodeField *gifield = + (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD); + node->members = g_list_append (node->members, gifield); + gifield->node.name = member->ident; + gifield->type = create_node_from_ctype (member->base_type); + } + } + else + { + GIdlNodeUnion *node = + (GIdlNodeUnion *) g_idl_node_new (G_IDL_NODE_UNION); + char *lower_case_prefix; + GList *member_l; + + node->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + lower_case_prefix = g_ascii_strdown (sym->ident, -1); + g_hash_table_insert (igenerator->type_map, sym->ident, node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + + node->node.name = sym->ident; + for (member_l = union_type->child_list; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + GIdlNodeField *gifield = + (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD); + node->members = g_list_append (node->members, gifield); + gifield->node.name = member->ident; + gifield->type = create_node_from_ctype (member->base_type); + } + } +} + +static void +g_igenerator_process_enum_typedef (GIGenerator * igenerator, CSymbol * sym) +{ + CType *enum_type; + GList *member_l; + GIdlNodeEnum *node; + CSymbol *enum_symbol; + + enum_type = sym->base_type; + if (enum_type->child_list == NULL) + { + g_assert (enum_type->name != NULL); + enum_symbol = + g_hash_table_lookup (igenerator->struct_or_union_or_enum_table, + enum_type->name); + if (enum_symbol != NULL) + { + enum_type = enum_symbol->base_type; + } + } + if (enum_type->child_list == NULL) + { + /* opaque type */ + return; + } + + node = g_hash_table_lookup (igenerator->type_map, sym->ident); + if (node != NULL) + { + return; + } + + node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_ENUM); + node->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + + for (member_l = enum_type->child_list; member_l != NULL; + member_l = member_l->next) + { + CSymbol *member = member_l->data; + GIdlNodeValue *gival = + (GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE); + node->values = g_list_append (node->values, gival); + gival->node.name = member->ident; + gival->value = member->const_int; + } +} + +static void +g_igenerator_process_function_typedef (GIGenerator * igenerator, + CSymbol * sym) +{ + GList *param_l; + int i; + + /* handle callback types */ + GIdlNodeFunction *gifunc = + (GIdlNodeFunction *) g_idl_node_new (G_IDL_NODE_CALLBACK); + + gifunc->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, gifunc, + (GCompareFunc) g_idl_node_cmp); + + gifunc->symbol = sym->ident; + gifunc->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + gifunc->result->type = + create_node_from_ctype (sym->base_type->base_type->base_type); + + for (param_l = sym->base_type->base_type->child_list, i = 1; + param_l != NULL; param_l = param_l->next, i++) + { + CSymbol *param_sym = param_l->data; + GIdlNodeParam *param = + (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM); + if (param_sym->ident == NULL) + { + param->node.name = g_strdup_printf ("p%d", i); + } + else + { + param->node.name = param_sym->ident; + } + param->type = create_node_from_ctype (param_sym->base_type); + gifunc->parameters = g_list_append (gifunc->parameters, param); + } +} + +static void +g_igenerator_process_constant (GIGenerator * igenerator, CSymbol * sym) +{ + GIdlNodeConstant *giconst = + (GIdlNodeConstant *) g_idl_node_new (G_IDL_NODE_CONSTANT); + giconst->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, giconst, + (GCompareFunc) g_idl_node_cmp); + + giconst->type = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE); + if (sym->const_int_set) + { + giconst->type->unparsed = g_strdup ("int"); + giconst->value = g_strdup_printf ("%d", sym->const_int); + } + else if (sym->const_string != NULL) + { + giconst->type->unparsed = g_strdup ("char*"); + giconst->value = sym->const_string; + } +} + +static void +g_igenerator_process_symbols (GIGenerator * igenerator) +{ + GList *l; + /* process type symbols first to ensure complete type hashtables */ + /* type symbols */ + for (l = igenerator->symbol_list; l != NULL; l = l->next) + { + CSymbol *sym = l->data; + if (sym->ident[0] == '_') + { + /* ignore private / reserved symbols */ + continue; + } + if (sym->type == CSYMBOL_TYPE_TYPEDEF) + { + if (sym->base_type->type == CTYPE_STRUCT) + { + g_igenerator_process_struct_typedef (igenerator, sym); + } + else if (sym->base_type->type == CTYPE_UNION) + { + g_igenerator_process_union_typedef (igenerator, sym); + } + else if (sym->base_type->type == CTYPE_ENUM) + { + g_igenerator_process_enum_typedef (igenerator, sym); + } + else if (sym->base_type->type == CTYPE_POINTER + && sym->base_type->base_type->type == CTYPE_FUNCTION) + { + g_igenerator_process_function_typedef (igenerator, sym); + } + else + { + GIdlNodeStruct *node = + (GIdlNodeStruct *) g_idl_node_new (G_IDL_NODE_STRUCT); + char *lower_case_prefix; + + node->node.name = sym->ident; + igenerator->module->entries = + g_list_insert_sorted (igenerator->module->entries, node, + (GCompareFunc) g_idl_node_cmp); + lower_case_prefix = g_ascii_strdown (sym->ident, -1); + g_hash_table_insert (igenerator->type_map, sym->ident, node); + g_hash_table_insert (igenerator->type_by_lower_case_prefix, + lower_case_prefix, node); + } + } + } + /* other symbols */ + for (l = igenerator->symbol_list; l != NULL; l = l->next) + { + CSymbol *sym = l->data; + if (sym->ident[0] == '_') + { + /* ignore private / reserved symbols */ + continue; + } + if (sym->type == CSYMBOL_TYPE_FUNCTION) + { + g_igenerator_process_function_symbol (igenerator, sym); + } + else if (sym->type == CSYMBOL_TYPE_CONST) + { + g_igenerator_process_constant (igenerator, sym); + } + } +} + +void +g_igenerator_add_symbol (GIGenerator * igenerator, CSymbol * symbol) +{ + /* only add symbols of main file */ + gboolean found_filename = FALSE; + GList *l; + for (l = igenerator->filenames; l != NULL; l = l->next) + { + if (strcmp (l->data, igenerator->current_filename) == 0) + { + found_filename = TRUE; + break; + } + } + + symbol->directives = g_slist_reverse (igenerator->directives); + igenerator->directives = NULL; + + if (found_filename || igenerator->macro_scan) + { + igenerator->symbol_list = + g_list_prepend (igenerator->symbol_list, symbol); + } + + if (symbol->type == CSYMBOL_TYPE_TYPEDEF) + + { + g_hash_table_insert (igenerator->typedef_table, symbol->ident, symbol); + } + else if (symbol->type == CSYMBOL_TYPE_STRUCT + || symbol->type == CSYMBOL_TYPE_UNION + || symbol->type == CSYMBOL_TYPE_ENUM) + { + g_hash_table_insert (igenerator->struct_or_union_or_enum_table, + symbol->ident, symbol); + } +} + +gboolean +g_igenerator_is_typedef (GIGenerator * igenerator, const char *name) +{ + gboolean b = g_hash_table_lookup (igenerator->typedef_table, name) != NULL; + return b; +} + +void +g_igenerator_generate (GIGenerator * igenerator, + const gchar * filename, + GList *libraries) +{ + GList *l; + + for (l = igenerator->symbol_list; l != NULL; l = l->next) + { + CSymbol *sym = l->data; + if (sym->type == CSYMBOL_TYPE_FUNCTION + && g_str_has_suffix (sym->ident, "_get_type")) + { + if (sym->base_type->child_list == NULL) + { + // ignore get_type functions with parameters + igenerator->get_type_symbols = + g_list_prepend (igenerator->get_type_symbols, sym->ident); + } + } + } + + /* ensure to initialize GObject */ + g_type_class_ref (G_TYPE_OBJECT); + + for (l = libraries; l; l = l->next) + g_igenerator_process_module (igenerator, (const gchar*)l->data); + + g_igenerator_process_symbols (igenerator); + + g_idl_writer_save_file (igenerator->module, filename); +} + +static int +eat_hspace (FILE * f) +{ + int c; + do + { + c = fgetc (f); + } + while (c == ' ' || c == '\t'); + return c; +} + +static int +eat_line (FILE * f, int c) +{ + while (c != EOF && c != '\n') + { + c = fgetc (f); + } + if (c == '\n') + { + c = fgetc (f); + if (c == ' ' || c == '\t') + { + c = eat_hspace (f); + } + } + return c; +} + +static int +read_identifier (FILE * f, int c, char **identifier) +{ + GString *id = g_string_new (""); + while (isalnum (c) || c == '_') + { + g_string_append_c (id, c); + c = fgetc (f); + } + *identifier = g_string_free (id, FALSE); + return c; +} + +static void +g_igenerator_parse_macros (GIGenerator * igenerator) +{ + GError *error = NULL; + char *tmp_name = NULL; + FILE *fmacros = + fdopen (g_file_open_tmp ("gen-introspect-XXXXXX.h", &tmp_name, &error), + "w+"); + g_unlink (tmp_name); + + GList *l; + for (l = igenerator->filenames; l != NULL; l = l->next) + { + FILE *f = fopen (l->data, "r"); + int line = 1; + + GString *define_line; + char *str; + gboolean error_line = FALSE; + int c = eat_hspace (f); + while (c != EOF) + { + if (c != '#') + { + /* ignore line */ + c = eat_line (f, c); + line++; + continue; + } + + /* print current location */ + str = g_strescape (l->data, ""); + fprintf (fmacros, "# %d \"%s\"\n", line, str); + g_free (str); + + c = eat_hspace (f); + c = read_identifier (f, c, &str); + if (strcmp (str, "define") != 0 || (c != ' ' && c != '\t')) + { + g_free (str); + /* ignore line */ + c = eat_line (f, c); + line++; + continue; + } + g_free (str); + c = eat_hspace (f); + c = read_identifier (f, c, &str); + if (strlen (str) == 0 || (c != ' ' && c != '\t' && c != '(')) + { + g_free (str); + /* ignore line */ + c = eat_line (f, c); + line++; + continue; + } + define_line = g_string_new ("#define "); + g_string_append (define_line, str); + g_free (str); + if (c == '(') + { + while (c != ')') + { + g_string_append_c (define_line, c); + c = fgetc (f); + if (c == EOF || c == '\n') + { + error_line = TRUE; + break; + } + } + if (error_line) + { + g_string_free (define_line, TRUE); + /* ignore line */ + c = eat_line (f, c); + line++; + continue; + } + + g_assert (c == ')'); + g_string_append_c (define_line, c); + c = fgetc (f); + + /* found function-like macro */ + fprintf (fmacros, "%s\n", define_line->str); + + g_string_free (define_line, TRUE); + /* ignore rest of line */ + c = eat_line (f, c); + line++; + continue; + } + if (c != ' ' && c != '\t') + { + g_string_free (define_line, TRUE); + /* ignore line */ + c = eat_line (f, c); + line++; + continue; + } + while (c != EOF && c != '\n') + { + g_string_append_c (define_line, c); + c = fgetc (f); + if (c == '\\') + { + c = fgetc (f); + if (c == '\n') + { + /* fold lines when seeing backslash new-line sequence */ + c = fgetc (f); + } + else + { + g_string_append_c (define_line, '\\'); + } + } + } + + /* found object-like macro */ + fprintf (fmacros, "%s\n", define_line->str); + + c = eat_line (f, c); + line++; + } + + fclose (f); + } + + igenerator->macro_scan = TRUE; + rewind (fmacros); + + g_igenerator_parse_file (igenerator, fmacros); + fclose (fmacros); + + igenerator->macro_scan = FALSE; +} + +static void +g_igenerator_add_module (GIGenerator *igenerator, + GIdlModule *module) +{ + GList *l; + + for (l = module->entries; l; l = l->next) + { + GIdlNode *node = (GIdlNode*)l->data; + + if (node->type == G_IDL_NODE_OBJECT) + { + GIdlNodeInterface *object = (GIdlNodeInterface*)node; + gchar *name; + if (strcmp(module->name, igenerator->namespace) == 0) + name = g_strdup (node->name); + else + name = g_strdup_printf ("%s.%s", module->name, node->name); + g_hash_table_insert (igenerator->symbols, + g_strdup (object->gtype_name), + name); + } + } +} + +static void +g_igenerator_add_include_idl (GIGenerator *igenerator, + const gchar *filename) +{ + GList *l; + GList *modules; + + GError *error = NULL; + + modules = g_idl_parse_file (filename, &error); + if (error) + { + g_printerr ("An error occured while parsing %s: %s\n", + filename, error->message); + return; + } + + for (l = modules; l; l = l->next) + { + GIdlModule *module = (GIdlModule*)l->data; + g_igenerator_add_module (igenerator, module); + } +} + +static FILE * +g_igenerator_start_preprocessor (GIGenerator *igenerator, + GList *cpp_options) +{ + int cpp_out = -1, cpp_in = -1; + int cpp_argc = 0; + char **cpp_argv; + GList *l; + GError *error = NULL; + FILE *f, *out; + GPid pid; + int status = 0; + int read_bytes; + int i; + char **buffer; + int tmp; + char *tmpname; + + cpp_argv = g_new0 (char *, g_list_length (cpp_options) + 4); + cpp_argv[cpp_argc++] = "cpp"; + cpp_argv[cpp_argc++] = "-C"; + + /* Disable GCC extensions as we cannot parse them yet */ + cpp_argv[cpp_argc++] = "-U__GNUC__"; + + for (l = cpp_options; l; l = l->next) + cpp_argv[cpp_argc++] = (char*)l->data; + + cpp_argv[cpp_argc++] = NULL; + + if (igenerator->verbose) + { + GString *args = g_string_new (""); + + for (i = 0; i < cpp_argc - 1; i++) + { + g_string_append (args, cpp_argv[i]); + if (i < cpp_argc - 2) + g_string_append_c (args, ' '); + } + + g_printf ("Executing '%s'\n", args->str); + g_string_free (args, FALSE); + } + g_spawn_async_with_pipes (NULL, cpp_argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &pid, &cpp_in, &cpp_out, NULL, &error); + + g_free (cpp_argv); + if (error != NULL) + { + g_error ("%s", error->message); + return NULL; + } + + f = fdopen (cpp_in, "w"); + + for (l = igenerator->filenames; l != NULL; l = l->next) + { + if (igenerator->verbose) + g_printf ("Pre-processing %s\n", (char*)l->data); + + fprintf (f, "#include <%s>\n", (char *) l->data); + + } + + fclose (f); + close (cpp_in); + + tmp = g_file_open_tmp (NULL, &tmpname, &error); + if (error != NULL) + { + g_error (error->message); + return NULL; + } + + buffer = g_malloc0 (4096 * sizeof (char)); + + while (1) + { + read_bytes = read (cpp_out, buffer, 4096); + if (read_bytes == 0) + break; + write (tmp, buffer, read_bytes); + } + + g_free (buffer); + + close (cpp_out); + + if (waitpid (pid, &status, 0) > 0) + { + if (status != 0) + { + g_spawn_close_pid (pid); + kill (pid, SIGKILL); + + g_error ("cpp returned error code: %d\n", status); + unlink (tmpname); + g_free (tmpname); + return NULL; + } + } + + f = fdopen (tmp, "r"); + if (!f) + { + g_error (strerror (errno)); + unlink (tmpname); + g_free (tmpname); + return NULL; + } + rewind (f); + unlink (tmpname); + g_free (tmpname); + + return f; +} + + +void +g_igenerator_set_verbose (GIGenerator *igenerator, + gboolean verbose) +{ + igenerator->verbose = verbose; +} + +int +main (int argc, char **argv) +{ + GOptionContext *ctx; + gchar *namespace = NULL; + gchar *shared_library = NULL; + gchar **include_idls = NULL; + gchar *output = NULL; + gboolean verbose = FALSE; + + GIGenerator *igenerator; + int gopt_argc, i; + char **gopt_argv; + GList *filenames = NULL; + GError *error = NULL; + GList *l, *libraries = NULL; + GList *cpp_options = NULL; + char *buffer; + size_t size; + FILE *tmp; + GOptionEntry entries[] = + { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Be verbose" }, + { "output", 'o', 0, G_OPTION_ARG_STRING, &output, + "write output here instead of stdout", "FILE" }, + { "namespace", 'n', 0, G_OPTION_ARG_STRING, &namespace, + "Namespace of the module, like 'Gtk'", "NAMESPACE" }, + { "shared-library", 0, 0, G_OPTION_ARG_FILENAME, &shared_library, + "Shared library which contains the symbols", "FILE" }, + { "include-idl", 0, 0, G_OPTION_ARG_STRING_ARRAY, &include_idls, + "Other gidls to include", "IDL" }, + { NULL } + }; + + gopt_argc = 1; + gopt_argv = (char**)g_malloc (argc * sizeof (char*)); + gopt_argv[0] = argv[0]; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'I': + case 'D': + case 'U': + cpp_options = g_list_prepend (cpp_options, g_strdup (argv[i])); + break; + default: + gopt_argv[gopt_argc++] = argv[i]; + break; + } + } + else if (g_str_has_suffix (argv[i], ".h")) + { + gchar* filename; + + if (!g_path_is_absolute (argv[i])) + { + gchar *dir = g_get_current_dir (); + filename = g_strdup_printf ("%s/%s", dir, + argv[i]); + g_free (dir); + } + else + filename = g_strdup (argv[i]); + + filenames = g_list_append (filenames, filename); + } + else if (g_str_has_suffix (argv[i], ".la") || + g_str_has_suffix (argv[i], ".so") || + g_str_has_suffix (argv[i], ".dll")) + { + libraries = g_list_prepend (libraries, g_strdup (argv[i])); + } + else + { + gopt_argv[gopt_argc++] = argv[i]; + } + } + + ctx = g_option_context_new (""); + g_option_context_add_main_entries (ctx, entries, NULL); + + if (!g_option_context_parse (ctx, &gopt_argc, &gopt_argv, &error)) + { + g_printerr ("Parsing error: %s\n", error->message); + g_option_context_free (ctx); + return 1; + } + + g_free (gopt_argv); + g_option_context_free (ctx); + + if (!namespace) + { + g_printerr ("ERROR: namespace must be specified\n"); + return 1; + } + + igenerator = g_igenerator_new (namespace, shared_library); + + if (verbose) + g_igenerator_set_verbose (igenerator, TRUE); + + if (!filenames) + { + g_printerr ("ERROR: Need at least one header file.\n"); + g_igenerator_free (igenerator); + return 0; + } + igenerator->filenames = filenames; + cpp_options = g_list_reverse (cpp_options); + libraries = g_list_reverse (libraries); + + g_type_init (); + + /* initialize threading as this may be required by libraries that we'll use + * libsoup-2.2 is an example of that. + */ + g_thread_init (NULL); + + if (include_idls) + { + for (i = 0; i < g_strv_length (include_idls); i++) + g_igenerator_add_include_idl (igenerator, include_idls[i]); + } + + tmp = g_igenerator_start_preprocessor (igenerator, cpp_options); + if (!tmp) + { + g_error ("ERROR in pre-processor.\n"); + g_igenerator_free (igenerator); + return 1; + } + + if (!g_igenerator_parse_file (igenerator, tmp)) + { + fclose (tmp); + g_igenerator_free (igenerator); + return 1; + } + + g_igenerator_parse_macros (igenerator); + + g_igenerator_generate (igenerator, output, libraries); + + fclose (tmp); + g_igenerator_free (igenerator); + + return 0; +} + diff --git a/tools/scanner.h b/tools/scanner.h new file mode 100644 index 00000000..b2140759 --- /dev/null +++ b/tools/scanner.h @@ -0,0 +1,167 @@ +/* GObject introspection: gen-introspect + * + * Copyright (C) 2007 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: + * Jürg Billeter <j@bitron.ch> + */ + +#ifndef __GEN_INTROSPECT_H__ +#define __GEN_INTROSPECT_H__ + +#include <glib.h> +#include "gidlmodule.h" + +G_BEGIN_DECLS typedef struct _GIGenerator GIGenerator; +typedef struct _CSymbol CSymbol; +typedef struct _CType CType; +typedef struct _CDirective CDirective; + +struct _GIGenerator +{ + /* parameters */ + char *namespace; + char *shared_library; + char *lower_case_namespace; + gboolean verbose; + + /* specified files to be parsed */ + GList *filenames; + /* source reference of current lexer position */ + char *current_filename; + GList *symbol_list; + GHashTable *typedef_table; + GHashTable *struct_or_union_or_enum_table; + + GIdlModule *module; + GList *get_type_symbols; + GHashTable *type_map; + GHashTable *type_by_lower_case_prefix; + + GHashTable *symbols; /* typename -> module.name */ + + /* scanner variables */ + gboolean macro_scan; + GSList *directives; /* list of CDirective for the current symbol */ +}; + +typedef enum +{ + CSYMBOL_TYPE_INVALID, + CSYMBOL_TYPE_CONST, + CSYMBOL_TYPE_OBJECT, + CSYMBOL_TYPE_FUNCTION, + CSYMBOL_TYPE_STRUCT, + CSYMBOL_TYPE_UNION, + CSYMBOL_TYPE_ENUM, + CSYMBOL_TYPE_TYPEDEF +} CSymbolType; + +struct _CSymbol +{ + CSymbolType type; + int id; + char *ident; + CType *base_type; + gboolean const_int_set; + int const_int; + char *const_string; + GSList *directives; /* list of CDirective */ +}; + +typedef enum +{ + CTYPE_INVALID, + CTYPE_VOID, + CTYPE_BASIC_TYPE, + CTYPE_TYPEDEF, + CTYPE_STRUCT, + CTYPE_UNION, + CTYPE_ENUM, + CTYPE_POINTER, + CTYPE_ARRAY, + CTYPE_FUNCTION +} CTypeType; + +typedef enum +{ + STORAGE_CLASS_NONE = 0, + STORAGE_CLASS_TYPEDEF = 1 << 1, + STORAGE_CLASS_EXTERN = 1 << 2, + STORAGE_CLASS_STATIC = 1 << 3, + STORAGE_CLASS_AUTO = 1 << 4, + STORAGE_CLASS_REGISTER = 1 << 5 +} StorageClassSpecifier; + +typedef enum +{ + TYPE_QUALIFIER_NONE = 0, + TYPE_QUALIFIER_CONST = 1 << 1, + TYPE_QUALIFIER_RESTRICT = 1 << 2, + TYPE_QUALIFIER_VOLATILE = 1 << 3 +} TypeQualifier; + +typedef enum +{ + FUNCTION_NONE = 0, + FUNCTION_INLINE = 1 << 1 +} FunctionSpecifier; + +typedef enum +{ + UNARY_ADDRESS_OF, + UNARY_POINTER_INDIRECTION, + UNARY_PLUS, + UNARY_MINUS, + UNARY_BITWISE_COMPLEMENT, + UNARY_LOGICAL_NEGATION +} UnaryOperator; + +struct _CType +{ + CTypeType type; + StorageClassSpecifier storage_class_specifier; + TypeQualifier type_qualifier; + FunctionSpecifier function_specifier; + char *name; + CType *base_type; + GList *child_list; +}; + +struct _CDirective { + char *name; + char *value; +}; + +CSymbol * csymbol_new (CSymbolType type); +gboolean csymbol_get_const_boolean (CSymbol *symbol); +void csymbol_free (CSymbol *symbol); +CDirective * cdirective_new (const gchar *name, + const gchar *value); +void cdirective_free (CDirective *directive); + +gboolean g_igenerator_parse_file (GIGenerator *igenerator, + FILE *file); +void g_igenerator_set_verbose (GIGenerator *igenerator, + gboolean verbose); +void g_igenerator_add_symbol (GIGenerator *igenerator, + CSymbol *symbol); +gboolean g_igenerator_is_typedef (GIGenerator *igenerator, + const char *name); +G_END_DECLS +#endif diff --git a/tools/scannerlexer.l b/tools/scannerlexer.l new file mode 100644 index 00000000..114b736c --- /dev/null +++ b/tools/scannerlexer.l @@ -0,0 +1,311 @@ +/* -*- Mode: C -*- +/* GObject introspection: C lexer + * + * Copyright (c) 1997 Sandro Sigala <ssigala@globalnet.it> + * Copyright (c) 2007-2008 Jürg Billeter <j@bitron.ch> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%{ +#include <ctype.h> +#include <stdio.h> + +#include "scanner.h" +#include "scannerparser.h" + +int lineno; + +extern int yylex (GIGenerator *igenerator); +#define YY_DECL int yylex (GIGenerator *igenerator) +static int yywrap (void); +static void parse_comment (GIGenerator *igenerator); +static void process_directive (GIGenerator *igenerator); +static int check_identifier (GIGenerator *igenerator, const char *); +%} + +intsuffix ([uU][lL]?)|([lL][uU]?) +fracconst ([0-9]*\.[0-9]+)|([0-9]+\.) +exppart [eE][-+]?[0-9]+ +floatsuffix [fFlL] +chartext ([^\'])|(\\.) +stringtext ([^\"])|(\\.) + +%% + +"\n" { ++lineno; } /* " */ +[\t\f\v\r ]+ { /* Ignore whitespace. */ } + +"/*" { parse_comment(igenerator); } +"//".* { } + +"#define "[a-zA-Z_][a-zA-Z_0-9]*"(" { yyless (yyleng - 1); return FUNCTION_MACRO; } +"#define "[a-zA-Z_][a-zA-Z_0-9]* { return OBJECT_MACRO; } + +"#" { process_directive(igenerator); } + +"{" { return '{'; } +"<%" { return '{'; } +"}" { return '}'; } +"%>" { return '}'; } +"[" { return '['; } +"<:" { return '['; } +"]" { return ']'; } +":>" { return ']'; } +"(" { return '('; } +")" { return ')'; } +";" { return ';'; } +":" { return ':'; } +"..." { return ELLIPSIS; } +"?" { return '?'; } +"." { return '.'; } +"+" { return '+'; } +"-" { return '-'; } +"*" { return '*'; } +"/" { return '/'; } +"%" { return '%'; } +"^" { return '^'; } +"&" { return '&'; } +"|" { return '|'; } +"~" { return '~'; } +"!" { return '!'; } +"=" { return '='; } +"<" { return '<'; } +">" { return '>'; } +"+=" { return ADDEQ; } +"-=" { return SUBEQ; } +"*=" { return MULEQ; } +"/=" { return DIVEQ; } +"%=" { return MODEQ; } +"^=" { return XOREQ; } +"&=" { return ANDEQ; } +"|=" { return OREQ; } +"<<" { return SL; } +">>" { return SR; } +"<<=" { return SLEQ; } +">>=" { return SREQ; } +"==" { return EQ; } +"!=" { return NOTEQ; } +"<=" { return LTEQ; } +">=" { return GTEQ; } +"&&" { return ANDAND; } +"||" { return OROR; } +"++" { return PLUSPLUS; } +"--" { return MINUSMINUS; } +"," { return ','; } +"->" { return ARROW; } + +[a-zA-Z_][a-zA-Z_0-9]* { if (igenerator->macro_scan) return IDENTIFIER; else REJECT; } + +"auto" { return AUTO; } +"break" { return BREAK; } +"case" { return CASE; } +"char" { return CHAR; } +"const" { return CONST; } +"continue" { return CONTINUE; } +"default" { return DEFAULT; } +"do" { return DO; } +"double" { return DOUBLE; } +"else" { return ELSE; } +"enum" { return ENUM; } +"extern" { return EXTERN; } +"float" { return FLOAT; } +"for" { return FOR; } +"goto" { return GOTO; } +"if" { return IF; } +"inline" { return INLINE; } +"int" { return INT; } +"long" { return LONG; } +"register" { return REGISTER; } +"restrict" { return RESTRICT; } +"return" { return RETURN; } +"short" { return SHORT; } +"signed" { return SIGNED; } +"sizeof" { return SIZEOF; } +"static" { return STATIC; } +"struct" { return STRUCT; } +"switch" { return SWITCH; } +"typedef" { return TYPEDEF; } +"union" { return UNION; } +"unsigned" { return UNSIGNED; } +"void" { return VOID; } +"volatile" { return VOLATILE; } +"while" { return WHILE; } + +[a-zA-Z_][a-zA-Z_0-9]* { return check_identifier(igenerator, yytext); } + +"0"[xX][0-9a-fA-F]+{intsuffix}? { return INTEGER; } +"0"[0-7]+{intsuffix}? { return INTEGER; } +[0-9]+{intsuffix}? { return INTEGER; } + +{fracconst}{exppart}?{floatsuffix}? { return FLOATING; } +[0-9]+{exppart}{floatsuffix}? { return FLOATING; } + +"'"{chartext}*"'" { return CHARACTER; } +"L'"{chartext}*"'" { return CHARACTER; } + +"\""{stringtext}*"\"" { return STRING; } +"L\""{stringtext}*"\"" { return STRING; } + +. { fprintf(stderr, "%s:%d: unexpected character `%c'\n", igenerator->current_filename, lineno, yytext[0]); } + +%% + +static int yywrap (void) +{ + return 1; +} + +static void parse_gtkdoc (GIGenerator *igenerator, int *c1, int *c2) +{ + gboolean isline = FALSE; + gchar line[256]; + int i; + gchar **parts; + CDirective *directive; + char *name, *value; + + i = 0; + do { + *c1 = *c2; + if (*c1 == '\n') + { + isline = TRUE; + break; + } + if (i >= 256) + break; + line[i++] = *c1; + *c2 = input(); + + } while (*c2 != EOF && !(*c1 == '*' && *c2 == '/')); + + if (!isline) + return; + + line[i] = '\0'; + + parts = g_strsplit (line, ": ", 2); + + if (g_strv_length (parts) == 2) + { + name = parts[0]; + value = parts[1]; + } + else /* parts == 1 */ + { + name = parts[0]; + value = NULL; + } + + directive = cdirective_new (name, value); + igenerator->directives = g_slist_prepend (igenerator->directives, + directive); + + g_strfreev (parts); +} + +static void parse_comment (GIGenerator *igenerator) +{ + int c1, c2; + + c1 = input(); + c2 = input(); + + while (c2 != EOF && !(c1 == '*' && c2 == '/')) + { + if (c1 == '\n') + ++lineno; + c1 = c2; + c2 = input(); + + if (c1 == ' ' && c2 == '@') + { + c1 = c2; + c2 = input(); + parse_gtkdoc (igenerator, &c1, &c2); + } + } +} + +static int check_identifier (GIGenerator *igenerator, const char *s) +{ + /* + * This function checks if `s' is a type name or an + * identifier. + */ + + if (g_igenerator_is_typedef (igenerator, s)) { + return TYPEDEF_NAME; + } else if (strcmp (s, "__builtin_va_list") == 0) { + return TYPEDEF_NAME; + } + + return IDENTIFIER; +} + +static void process_directive (GIGenerator *igenerator) +{ + /* extract current filename from #line directives */ + GString *filename_builder; + gboolean in_string, found_filename; + + lineno = 0; + found_filename = FALSE; + in_string = FALSE; + filename_builder = g_string_new (""); + + int c = input (); + while (c != EOF && c != '\n') { + if (!in_string) { + if (c == '\"') { + in_string = TRUE; + found_filename = TRUE; + } else if (c >= '0' && c <= '9') { + if (!found_filename) { + lineno = lineno * 10 + (c - '0'); + } + } + } else { + if (c == '\"') { + in_string = FALSE; + } else if (c == '\\') { + g_string_append_c (filename_builder, c); + c = input (); + g_string_append_c (filename_builder, c); + } else { + g_string_append_c (filename_builder, c); + } + } + c = input (); + } + + if (filename_builder->len > 0) { + char *filename = g_strcompress (filename_builder->str); + g_free (igenerator->current_filename); + igenerator->current_filename = filename; + } + + g_string_free (filename_builder, TRUE); +} + diff --git a/tools/scannerparser.y b/tools/scannerparser.y new file mode 100644 index 00000000..fae5067d --- /dev/null +++ b/tools/scannerparser.y @@ -0,0 +1,1376 @@ +/* GObject introspection: C parser + * + * Copyright (c) 1997 Sandro Sigala <ssigala@globalnet.it> + * Copyright (c) 2007 Jürg Billeter <j@bitron.ch> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%{ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "scanner.h" + +extern FILE *yyin; +extern int lineno; +extern char *yytext; + +extern int yylex (GIGenerator *igenerator); +static void yyerror(GIGenerator *igenerator, const char *s); + +static int last_enum_value = -1; +static GHashTable *const_table = NULL; + +CSymbol * +csymbol_new (CSymbolType type) +{ + CSymbol *s = g_new0 (CSymbol, 1); + s->type = type; + return s; +} + +static void +ctype_free (CType * type) +{ + g_free (type); + g_free (type->name); + g_list_foreach (type->child_list, (GFunc)ctype_free, NULL); + g_list_free (type->child_list); +} + +void +csymbol_free (CSymbol * symbol) +{ + g_free (symbol->ident); + ctype_free (symbol->base_type); + g_free (symbol->const_string); + g_free (symbol); + g_slist_foreach (symbol->directives, (GFunc)cdirective_free, NULL); + g_slist_free (symbol->directives); +} + +gboolean +csymbol_get_const_boolean (CSymbol * symbol) +{ + return (symbol->const_int_set && symbol->const_int) || symbol->const_string; +} + +CType * +ctype_new (CTypeType type) +{ + CType *t = g_new0 (CType, 1); + t->type = type; + return t; +} + +CType * +ctype_copy (CType * type) +{ + return g_memdup (type, sizeof (CType)); +} + +CType * +cbasic_type_new (const char *name) +{ + CType *basic_type = ctype_new (CTYPE_BASIC_TYPE); + basic_type->name = g_strdup (name); + return basic_type; +} + +CType * +ctypedef_new (const char *name) +{ + CType *typedef_ = ctype_new (CTYPE_TYPEDEF); + typedef_->name = g_strdup (name); + return typedef_; +} + +CType * +cstruct_new (const char *name) +{ + CType *struct_ = ctype_new (CTYPE_STRUCT); + struct_->name = g_strdup (name); + return struct_; +} + +CType * +cunion_new (const char *name) +{ + CType *union_ = ctype_new (CTYPE_UNION); + union_->name = g_strdup (name); + return union_; +} + +CType * +cenum_new (const char *name) +{ + CType *enum_ = ctype_new (CTYPE_ENUM); + enum_->name = g_strdup (name); + return enum_; +} + +CType * +cpointer_new (CType * base_type) +{ + CType *pointer = ctype_new (CTYPE_POINTER); + pointer->base_type = ctype_copy (base_type); + return pointer; +} + +CType * +carray_new (void) +{ + CType *array = ctype_new (CTYPE_ARRAY); + return array; +} + +CType * +cfunction_new (void) +{ + CType *func = ctype_new (CTYPE_FUNCTION); + return func; +} + +/* use specified type as base type of symbol */ +static void +csymbol_merge_type (CSymbol *symbol, CType *type) +{ + CType **foundation_type = &(symbol->base_type); + while (*foundation_type != NULL) { + foundation_type = &((*foundation_type)->base_type); + } + *foundation_type = ctype_copy (type); +} + +CDirective * +cdirective_new (const gchar *name, + const gchar *value) +{ + CDirective *directive; + + directive = g_slice_new (CDirective); + directive->name = g_strdup (name); + directive->value = g_strdup (value); + return directive; +} + +void +cdirective_free (CDirective *directive) +{ + g_free (directive->name); + g_free (directive->value); + g_slice_free (CDirective, directive); +} + +%} + +%error-verbose +%union { + char *str; + GList *list; + CSymbol *symbol; + CType *ctype; + StorageClassSpecifier storage_class_specifier; + TypeQualifier type_qualifier; + FunctionSpecifier function_specifier; + UnaryOperator unary_operator; +} + +%parse-param { GIGenerator* igenerator } +%lex-param { GIGenerator* igenerator } + +%token <str> IDENTIFIER "identifier" +%token <str> TYPEDEF_NAME "typedef-name" + +%token INTEGER FLOATING CHARACTER STRING + +%token ELLIPSIS ADDEQ SUBEQ MULEQ DIVEQ MODEQ XOREQ ANDEQ OREQ SL SR +%token SLEQ SREQ EQ NOTEQ LTEQ GTEQ ANDAND OROR PLUSPLUS MINUSMINUS ARROW + +%token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM +%token EXTERN FLOAT FOR GOTO IF INLINE INT LONG REGISTER RESTRICT RETURN SHORT +%token SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE +%token WHILE + +%token FUNCTION_MACRO OBJECT_MACRO + +%start translation_unit + +%type <ctype> declaration_specifiers +%type <ctype> enum_specifier +%type <ctype> pointer +%type <ctype> specifier_qualifier_list +%type <ctype> struct_or_union +%type <ctype> struct_or_union_specifier +%type <ctype> type_specifier +%type <str> identifier +%type <str> typedef_name +%type <str> identifier_or_typedef_name +%type <symbol> abstract_declarator +%type <symbol> init_declarator +%type <symbol> declarator +%type <symbol> enumerator +%type <symbol> direct_abstract_declarator +%type <symbol> direct_declarator +%type <symbol> parameter_declaration +%type <symbol> struct_declarator +%type <list> enumerator_list +%type <list> identifier_list +%type <list> init_declarator_list +%type <list> parameter_type_list +%type <list> parameter_list +%type <list> struct_declaration +%type <list> struct_declaration_list +%type <list> struct_declarator_list +%type <storage_class_specifier> storage_class_specifier +%type <type_qualifier> type_qualifier +%type <type_qualifier> type_qualifier_list +%type <function_specifier> function_specifier +%type <symbol> expression +%type <symbol> constant_expression +%type <symbol> conditional_expression +%type <symbol> logical_and_expression +%type <symbol> logical_or_expression +%type <symbol> inclusive_or_expression +%type <symbol> exclusive_or_expression +%type <symbol> multiplicative_expression +%type <symbol> additive_expression +%type <symbol> shift_expression +%type <symbol> relational_expression +%type <symbol> equality_expression +%type <symbol> and_expression +%type <symbol> cast_expression +%type <symbol> assignment_expression +%type <symbol> unary_expression +%type <symbol> postfix_expression +%type <symbol> primary_expression +%type <unary_operator> unary_operator +%type <str> function_macro +%type <str> object_macro +%type <symbol> strings + +%% + +/* A.2.1 Expressions. */ + +primary_expression + : identifier + { + $$ = g_hash_table_lookup (const_table, $1); + if ($$ == NULL) { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + } + | INTEGER + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + if (g_str_has_prefix (yytext, "0x") && strlen (yytext) > 2) { + $$->const_int = strtol (yytext + 2, NULL, 16); + } else if (g_str_has_prefix (yytext, "0") && strlen (yytext) > 1) { + $$->const_int = strtol (yytext + 1, NULL, 8); + } else { + $$->const_int = atoi (yytext); + } + } + | CHARACTER + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | FLOATING + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | strings + | '(' expression ')' + { + $$ = $2; + } + ; + +/* concatenate adjacent string literal tokens */ +strings + : STRING + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + yytext[strlen (yytext) - 1] = '\0'; + $$->const_string = g_strcompress (yytext + 1); + } + | strings STRING + { + char *strings, *string2; + $$ = $1; + yytext[strlen (yytext) - 1] = '\0'; + string2 = g_strcompress (yytext + 1); + strings = g_strconcat ($$->const_string, string2, NULL); + g_free ($$->const_string); + g_free (string2); + $$->const_string = strings; + } + ; + +identifier + : IDENTIFIER + { + $$ = g_strdup (yytext); + } + ; + +identifier_or_typedef_name + : identifier + | typedef_name + ; + +postfix_expression + : primary_expression + | postfix_expression '[' expression ']' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression '(' argument_expression_list ')' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression '(' ')' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression '.' identifier_or_typedef_name + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression ARROW identifier_or_typedef_name + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression PLUSPLUS + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | postfix_expression MINUSMINUS + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + ; + +argument_expression_list + : assignment_expression + | argument_expression_list ',' assignment_expression + ; + +unary_expression + : postfix_expression + | PLUSPLUS unary_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | MINUSMINUS unary_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | unary_operator cast_expression + { + switch ($1) { + case UNARY_PLUS: + $$ = $2; + break; + case UNARY_MINUS: + $$ = $2; + $$->const_int = -$2->const_int; + break; + case UNARY_BITWISE_COMPLEMENT: + $$ = $2; + $$->const_int = ~$2->const_int; + break; + case UNARY_LOGICAL_NEGATION: + $$ = $2; + $$->const_int = !csymbol_get_const_boolean ($2); + break; + default: + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + break; + } + } + | SIZEOF unary_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | SIZEOF '(' type_name ')' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + ; + +unary_operator + : '&' + { + $$ = UNARY_ADDRESS_OF; + } + | '*' + { + $$ = UNARY_POINTER_INDIRECTION; + } + | '+' + { + $$ = UNARY_PLUS; + } + | '-' + { + $$ = UNARY_MINUS; + } + | '~' + { + $$ = UNARY_BITWISE_COMPLEMENT; + } + | '!' + { + $$ = UNARY_LOGICAL_NEGATION; + } + ; + +cast_expression + : unary_expression + | '(' type_name ')' cast_expression + { + $$ = $4; + } + ; + +multiplicative_expression + : cast_expression + | multiplicative_expression '*' cast_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int * $3->const_int; + } + | multiplicative_expression '/' cast_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + if ($3->const_int != 0) { + $$->const_int = $1->const_int / $3->const_int; + } + } + | multiplicative_expression '%' cast_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int % $3->const_int; + } + ; + +additive_expression + : multiplicative_expression + | additive_expression '+' multiplicative_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int + $3->const_int; + } + | additive_expression '-' multiplicative_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int - $3->const_int; + } + ; + +shift_expression + : additive_expression + | shift_expression SL additive_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int << $3->const_int; + } + | shift_expression SR additive_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int >> $3->const_int; + } + ; + +relational_expression + : shift_expression + | relational_expression '<' shift_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int < $3->const_int; + } + | relational_expression '>' shift_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int > $3->const_int; + } + | relational_expression LTEQ shift_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int <= $3->const_int; + } + | relational_expression GTEQ shift_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int >= $3->const_int; + } + ; + +equality_expression + : relational_expression + | equality_expression EQ relational_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int == $3->const_int; + } + | equality_expression NOTEQ relational_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int != $3->const_int; + } + ; + +and_expression + : equality_expression + | and_expression '&' equality_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int & $3->const_int; + } + ; + +exclusive_or_expression + : and_expression + | exclusive_or_expression '^' and_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int ^ $3->const_int; + } + ; + +inclusive_or_expression + : exclusive_or_expression + | inclusive_or_expression '|' exclusive_or_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = $1->const_int | $3->const_int; + } + ; + +logical_and_expression + : inclusive_or_expression + | logical_and_expression ANDAND inclusive_or_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = csymbol_get_const_boolean ($1) && csymbol_get_const_boolean ($3); + } + ; + +logical_or_expression + : logical_and_expression + | logical_or_expression OROR logical_and_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_CONST); + $$->const_int_set = TRUE; + $$->const_int = csymbol_get_const_boolean ($1) || csymbol_get_const_boolean ($3); + } + ; + +conditional_expression + : logical_or_expression + | logical_or_expression '?' expression ':' conditional_expression + { + $$ = csymbol_get_const_boolean ($1) ? $3 : $5; + } + ; + +assignment_expression + : conditional_expression + | unary_expression assignment_operator assignment_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + ; + +assignment_operator + : '=' + | MULEQ + | DIVEQ + | MODEQ + | ADDEQ + | SUBEQ + | SLEQ + | SREQ + | ANDEQ + | XOREQ + | OREQ + ; + +expression + : assignment_expression + | expression ',' assignment_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + ; + +constant_expression + : conditional_expression + ; + +/* A.2.2 Declarations. */ + +declaration + : declaration_specifiers init_declarator_list ';' + { + GList *l; + for (l = $2; l != NULL; l = l->next) { + CSymbol *sym = l->data; + csymbol_merge_type (sym, $1); + if ($1->storage_class_specifier & STORAGE_CLASS_TYPEDEF) { + sym->type = CSYMBOL_TYPE_TYPEDEF; + } else if (sym->base_type->type == CTYPE_FUNCTION) { + sym->type = CSYMBOL_TYPE_FUNCTION; + } else { + sym->type = CSYMBOL_TYPE_OBJECT; + } + g_igenerator_add_symbol (igenerator, sym); + } + } + | declaration_specifiers ';' + ; + +declaration_specifiers + : storage_class_specifier declaration_specifiers + { + $$ = $2; + $$->storage_class_specifier |= $1; + } + | storage_class_specifier + { + $$ = ctype_new (CTYPE_INVALID); + $$->storage_class_specifier |= $1; + } + | type_specifier declaration_specifiers + { + $$ = $1; + $$->base_type = $2; + } + | type_specifier + | type_qualifier declaration_specifiers + { + $$ = $2; + $$->type_qualifier |= $1; + } + | type_qualifier + { + $$ = ctype_new (CTYPE_INVALID); + $$->type_qualifier |= $1; + } + | function_specifier declaration_specifiers + { + $$ = $2; + $$->function_specifier |= $1; + } + | function_specifier + { + $$ = ctype_new (CTYPE_INVALID); + $$->function_specifier |= $1; + } + ; + +init_declarator_list + : init_declarator + { + $$ = g_list_append (NULL, $1); + } + | init_declarator_list ',' init_declarator + { + $$ = g_list_append ($1, $3); + } + ; + +init_declarator + : declarator + | declarator '=' initializer + ; + +storage_class_specifier + : TYPEDEF + { + $$ = STORAGE_CLASS_TYPEDEF; + } + | EXTERN + { + $$ = STORAGE_CLASS_EXTERN; + } + | STATIC + { + $$ = STORAGE_CLASS_STATIC; + } + | AUTO + { + $$ = STORAGE_CLASS_AUTO; + } + | REGISTER + { + $$ = STORAGE_CLASS_REGISTER; + } + ; + +type_specifier + : VOID + { + $$ = ctype_new (CTYPE_VOID); + } + | CHAR + { + $$ = cbasic_type_new ("char"); + } + | SHORT + { + $$ = cbasic_type_new ("short"); + } + | INT + { + $$ = cbasic_type_new ("int"); + } + | LONG + { + $$ = cbasic_type_new ("long"); + } + | FLOAT + { + $$ = cbasic_type_new ("float"); + } + | DOUBLE + { + $$ = cbasic_type_new ("double"); + } + | SIGNED + { + $$ = cbasic_type_new ("signed"); + } + | UNSIGNED + { + $$ = cbasic_type_new ("unsigned"); + } + | struct_or_union_specifier + | enum_specifier + | typedef_name + { + $$ = ctypedef_new ($1); + } + ; + +struct_or_union_specifier + : struct_or_union identifier_or_typedef_name '{' struct_declaration_list '}' + { + $$ = $1; + $$->name = $2; + $$->child_list = $4; + + CSymbol *sym = csymbol_new (CSYMBOL_TYPE_INVALID); + if ($$->type == CTYPE_STRUCT) { + sym->type = CSYMBOL_TYPE_STRUCT; + } else if ($$->type == CTYPE_UNION) { + sym->type = CSYMBOL_TYPE_UNION; + } else { + g_assert_not_reached (); + } + sym->ident = g_strdup ($$->name); + sym->base_type = ctype_copy ($$); + g_igenerator_add_symbol (igenerator, sym); + } + | struct_or_union '{' struct_declaration_list '}' + { + $$ = $1; + $$->child_list = $3; + } + | struct_or_union identifier_or_typedef_name + { + $$ = $1; + $$->name = $2; + } + ; + +struct_or_union + : STRUCT + { + $$ = cstruct_new (NULL); + } + | UNION + { + $$ = cunion_new (NULL); + } + ; + +struct_declaration_list + : struct_declaration + | struct_declaration_list struct_declaration + { + $$ = g_list_concat ($1, $2); + } + ; + +struct_declaration + : specifier_qualifier_list struct_declarator_list ';' + { + GList *l; + $$ = NULL; + for (l = $2; l != NULL; l = l->next) { + CSymbol *sym = l->data; + if ($1->storage_class_specifier & STORAGE_CLASS_TYPEDEF) { + sym->type = CSYMBOL_TYPE_TYPEDEF; + } + csymbol_merge_type (sym, $1); + $$ = g_list_append ($$, sym); + } + } + ; + +specifier_qualifier_list + : type_specifier specifier_qualifier_list + { + $$ = $1; + $$->base_type = $2; + } + | type_specifier + | type_qualifier specifier_qualifier_list + { + $$ = $2; + $$->type_qualifier |= $1; + } + | type_qualifier + { + $$ = ctype_new (CTYPE_INVALID); + $$->type_qualifier |= $1; + } + ; + +struct_declarator_list + : struct_declarator + { + $$ = g_list_append (NULL, $1); + } + | struct_declarator_list ',' struct_declarator + { + $$ = g_list_append ($1, $3); + } + ; + +struct_declarator + : /* empty, support for anonymous structs and unions */ + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | declarator + | ':' constant_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + } + | declarator ':' constant_expression + ; + +enum_specifier + : ENUM identifier_or_typedef_name '{' enumerator_list '}' + { + $$ = cenum_new ($2); + $$->child_list = $4; + last_enum_value = -1; + } + | ENUM '{' enumerator_list '}' + { + $$ = cenum_new (NULL); + $$->child_list = $3; + last_enum_value = -1; + } + | ENUM identifier_or_typedef_name '{' enumerator_list ',' '}' + { + $$ = cenum_new ($2); + $$->child_list = $4; + last_enum_value = -1; + } + | ENUM '{' enumerator_list ',' '}' + { + $$ = cenum_new (NULL); + $$->child_list = $3; + last_enum_value = -1; + } + | ENUM identifier_or_typedef_name + { + $$ = cenum_new ($2); + } + ; + +enumerator_list + : enumerator + { + $$ = g_list_append (NULL, $1); + } + | enumerator_list ',' enumerator + { + $$ = g_list_append ($1, $3); + } + ; + +enumerator + : identifier + { + $$ = csymbol_new (CSYMBOL_TYPE_OBJECT); + $$->ident = $1; + $$->const_int_set = TRUE; + $$->const_int = ++last_enum_value; + g_hash_table_insert (const_table, g_strdup ($$->ident), $$); + } + | identifier '=' constant_expression + { + $$ = csymbol_new (CSYMBOL_TYPE_OBJECT); + $$->ident = $1; + $$->const_int_set = TRUE; + $$->const_int = $3->const_int; + last_enum_value = $$->const_int; + g_hash_table_insert (const_table, g_strdup ($$->ident), $$); + } + ; + +type_qualifier + : CONST + { + $$ = TYPE_QUALIFIER_CONST; + } + | RESTRICT + { + $$ = TYPE_QUALIFIER_RESTRICT; + } + | VOLATILE + { + $$ = TYPE_QUALIFIER_VOLATILE; + } + ; + +function_specifier + : INLINE + { + $$ = FUNCTION_INLINE; + } + ; + +declarator + : pointer direct_declarator + { + $$ = $2; + csymbol_merge_type ($$, $1); + } + | direct_declarator + ; + +direct_declarator + : identifier + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + $$->ident = $1; + } + | '(' declarator ')' + { + $$ = $2; + } + | direct_declarator '[' assignment_expression ']' + { + $$ = $1; + csymbol_merge_type ($$, carray_new ()); + } + | direct_declarator '[' ']' + { + $$ = $1; + csymbol_merge_type ($$, carray_new ()); + } + | direct_declarator '(' parameter_type_list ')' + { + CType *func = cfunction_new (); + // ignore (void) parameter list + if ($3 != NULL && ($3->next != NULL || ((CSymbol *) $3->data)->base_type->type != CTYPE_VOID)) { + func->child_list = $3; + } + $$ = $1; + csymbol_merge_type ($$, func); + } + | direct_declarator '(' identifier_list ')' + { + CType *func = cfunction_new (); + func->child_list = $3; + $$ = $1; + csymbol_merge_type ($$, func); + } + | direct_declarator '(' ')' + { + CType *func = cfunction_new (); + $$ = $1; + csymbol_merge_type ($$, func); + } + ; + +pointer + : '*' type_qualifier_list + { + $$ = cpointer_new (NULL); + $$->type_qualifier = $2; + } + | '*' + { + $$ = cpointer_new (NULL); + } + | '*' type_qualifier_list pointer + { + $$ = cpointer_new ($3); + $$->type_qualifier = $2; + } + | '*' pointer + { + $$ = cpointer_new ($2); + } + ; + +type_qualifier_list + : type_qualifier + | type_qualifier_list type_qualifier + { + $$ = $1 | $2; + } + ; + +parameter_type_list + : parameter_list + | parameter_list ',' ELLIPSIS + ; + +parameter_list + : parameter_declaration + { + $$ = g_list_append (NULL, $1); + } + | parameter_list ',' parameter_declaration + { + $$ = g_list_append ($1, $3); + } + ; + +parameter_declaration + : declaration_specifiers declarator + { + $$ = $2; + csymbol_merge_type ($$, $1); + } + | declaration_specifiers abstract_declarator + { + $$ = $2; + csymbol_merge_type ($$, $1); + } + | declaration_specifiers + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + $$->base_type = $1; + } + ; + +identifier_list + : identifier + { + CSymbol *sym = csymbol_new (CSYMBOL_TYPE_INVALID); + sym->ident = $1; + $$ = g_list_append (NULL, sym); + } + | identifier_list ',' identifier + { + CSymbol *sym = csymbol_new (CSYMBOL_TYPE_INVALID); + sym->ident = $3; + $$ = g_list_append ($1, sym); + } + ; + +type_name + : specifier_qualifier_list + | specifier_qualifier_list abstract_declarator + ; + +abstract_declarator + : pointer + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + csymbol_merge_type ($$, $1); + } + | direct_abstract_declarator + | pointer direct_abstract_declarator + { + $$ = $2; + csymbol_merge_type ($$, $1); + } + ; + +direct_abstract_declarator + : '(' abstract_declarator ')' + { + $$ = $2; + } + | '[' ']' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + csymbol_merge_type ($$, carray_new ()); + } + | '[' assignment_expression ']' + { + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + csymbol_merge_type ($$, carray_new ()); + } + | direct_abstract_declarator '[' ']' + { + $$ = $1; + csymbol_merge_type ($$, carray_new ()); + } + | direct_abstract_declarator '[' assignment_expression ']' + { + $$ = $1; + csymbol_merge_type ($$, carray_new ()); + } + | '(' ')' + { + CType *func = cfunction_new (); + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + csymbol_merge_type ($$, func); + } + | '(' parameter_type_list ')' + { + CType *func = cfunction_new (); + // ignore (void) parameter list + if ($2 != NULL && ($2->next != NULL || ((CSymbol *) $2->data)->base_type->type != CTYPE_VOID)) { + func->child_list = $2; + } + $$ = csymbol_new (CSYMBOL_TYPE_INVALID); + csymbol_merge_type ($$, func); + } + | direct_abstract_declarator '(' ')' + { + CType *func = cfunction_new (); + $$ = $1; + csymbol_merge_type ($$, func); + } + | direct_abstract_declarator '(' parameter_type_list ')' + { + CType *func = cfunction_new (); + // ignore (void) parameter list + if ($3 != NULL && ($3->next != NULL || ((CSymbol *) $3->data)->base_type->type != CTYPE_VOID)) { + func->child_list = $3; + } + $$ = $1; + csymbol_merge_type ($$, func); + } + ; + +typedef_name + : TYPEDEF_NAME + { + $$ = g_strdup (yytext); + } + ; + +initializer + : assignment_expression + | '{' initializer_list '}' + | '{' initializer_list ',' '}' + ; + +initializer_list + : initializer + | initializer_list ',' initializer + ; + +/* A.2.3 Statements. */ + +statement + : labeled_statement + | compound_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + ; + +labeled_statement + : identifier_or_typedef_name ':' statement + | CASE constant_expression ':' statement + | DEFAULT ':' statement + ; + +compound_statement + : '{' '}' + | '{' block_item_list '}' + ; + +block_item_list + : block_item + | block_item_list block_item + ; + +block_item + : declaration + | statement + ; + +expression_statement + : ';' + | expression ';' + ; + +selection_statement + : IF '(' expression ')' statement + | IF '(' expression ')' statement ELSE statement + | SWITCH '(' expression ')' statement + ; + +iteration_statement + : WHILE '(' expression ')' statement + | DO statement WHILE '(' expression ')' ';' + | FOR '(' ';' ';' ')' statement + | FOR '(' expression ';' ';' ')' statement + | FOR '(' ';' expression ';' ')' statement + | FOR '(' expression ';' expression ';' ')' statement + | FOR '(' ';' ';' expression ')' statement + | FOR '(' expression ';' ';' expression ')' statement + | FOR '(' ';' expression ';' expression ')' statement + | FOR '(' expression ';' expression ';' expression ')' statement + ; + +jump_statement + : GOTO identifier_or_typedef_name ';' + | CONTINUE ';' + | BREAK ';' + | RETURN ';' + | RETURN expression ';' + ; + +/* A.2.4 External definitions. */ + +translation_unit + : external_declaration + | translation_unit external_declaration + ; + +external_declaration + : function_definition + | declaration + | macro + ; + +function_definition + : declaration_specifiers declarator declaration_list compound_statement + | declaration_specifiers declarator compound_statement + ; + +declaration_list + : declaration + | declaration_list declaration + ; + +/* Macros */ + +function_macro + : FUNCTION_MACRO + { + $$ = g_strdup (yytext + strlen ("#define ")); + } + ; + +object_macro + : OBJECT_MACRO + { + $$ = g_strdup (yytext + strlen ("#define ")); + } + ; + +function_macro_define + : function_macro '(' identifier_list ')' + ; + +object_macro_define + : object_macro constant_expression + { + if ($2->const_int_set || $2->const_string != NULL) { + $2->ident = $1; + g_igenerator_add_symbol (igenerator, $2); + } + } + ; + +macro + : function_macro_define + | object_macro_define + | error + ; + +%% + +static void +yyerror (GIGenerator *igenerator, const char *s) +{ + /* ignore errors while doing a macro scan as not all object macros + * have valid expressions */ + if (!igenerator->macro_scan) + { + fprintf(stderr, "%s:%d: %s\n", + igenerator->current_filename, lineno, s); + } +} + +gboolean +g_igenerator_parse_file (GIGenerator *igenerator, FILE *file) +{ + g_return_val_if_fail (file != NULL, FALSE); + + const_table = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + lineno = 1; + yyin = file; + yyparse (igenerator); + + g_hash_table_destroy (const_table); + const_table = NULL; + + yyin = NULL; + + return TRUE; +} + + |