ofdbus/dbus-glib/dbus/dbus-gparser.c
changeset 0 e4d67989cc36
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ofdbus/dbus-glib/dbus/dbus-gparser.c	Tue Feb 02 02:01:42 2010 +0200
@@ -0,0 +1,890 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gparser.c parse DBus description files
+ *
+ * Copyright (C) 2003, 2005  Red Hat, Inc.
+ * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "dbus-gparser.h"
+#include "dbus/dbus-glib-lowlevel.h"
+#include "dbus-gidl.h"
+#include "dbus-gobject.h"
+#include "dbus/dbus-signature.h"
+#include <string.h>
+
+
+
+#ifndef __SYMBIAN32__
+#include <libintl.h>
+#define _(x) gettext ((x))
+#define N_(x) x
+#else
+
+#define _(x) x
+#define N_(x) x
+#endif
+
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+typedef struct
+{
+  const char  *name;
+  const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (const char  *element_name,
+                   const char **attribute_names,
+                   const char **attribute_values,
+                   GError     **error,
+                   const char  *first_attribute_name,
+                   const char **first_attribute_retloc,
+                   ...)
+{
+  va_list args;
+  const char *name;
+  const char **retloc;
+  int n_attrs;
+#define MAX_ATTRS 24
+  LocateAttr attrs[MAX_ATTRS];
+  gboolean retval;
+  int i;
+
+  g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+  g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+  retval = TRUE;
+
+  n_attrs = 1;
+  attrs[0].name = first_attribute_name;
+  attrs[0].retloc = first_attribute_retloc;
+  *first_attribute_retloc = NULL;
+  
+  va_start (args, first_attribute_retloc);
+
+  name = va_arg (args, const char*);
+  retloc = va_arg (args, const char**);
+
+  while (name != NULL)
+    {
+      g_return_val_if_fail (retloc != NULL, FALSE);
+
+      g_assert (n_attrs < MAX_ATTRS);
+      
+      attrs[n_attrs].name = name;
+      attrs[n_attrs].retloc = retloc;
+      n_attrs += 1;
+      *retloc = NULL;      
+
+      name = va_arg (args, const char*);
+      retloc = va_arg (args, const char**);
+    }
+
+  va_end (args);
+
+  if (!retval)
+    return retval;
+
+  i = 0;
+  while (attribute_names[i])
+    {
+      int j;
+      gboolean found;
+
+      found = FALSE;
+      j = 0;
+      while (j < n_attrs)
+        {
+          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+            {
+              retloc = attrs[j].retloc;
+
+              if (*retloc != NULL)
+                {
+                  g_set_error (error,
+                               G_MARKUP_ERROR,
+                               G_MARKUP_ERROR_PARSE,
+                               _("Attribute \"%s\" repeated twice on the same <%s> element"),
+                               attrs[j].name, element_name);
+                  retval = FALSE;
+                  goto out;
+                }
+
+              *retloc = attribute_values[i];
+              found = TRUE;
+            }
+
+          ++j;
+        }
+
+      if (!found)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_PARSE,
+                       _("Attribute \"%s\" is invalid on <%s> element in this context"),
+                       attribute_names[i], element_name);
+          retval = FALSE;
+          goto out;
+        }
+
+      ++i;
+    }
+
+ out:
+  return retval;
+}
+
+#if 0
+static gboolean
+check_no_attributes (const char  *element_name,
+                     const char **attribute_names,
+                     const char **attribute_values,
+                     GError     **error)
+{
+  if (attribute_names[0] != NULL)
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Attribute \"%s\" is invalid on <%s> element in this context"),
+                   attribute_names[0], element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+#endif
+
+struct Parser
+{
+  int refcount;
+
+  NodeInfo *result; /* Filled in when we pop the last node */
+  GSList *node_stack;
+  InterfaceInfo *interface;
+  MethodInfo *method;
+  SignalInfo *signal;
+  PropertyInfo *property;
+  ArgInfo *arg;
+  gboolean in_annotation;
+};
+
+Parser*
+parser_new (void)
+{
+  Parser *parser;
+
+  parser = g_new0 (Parser, 1);
+
+  parser->refcount = 1;
+
+  return parser;
+}
+
+Parser *
+parser_ref (Parser *parser)
+{
+  parser->refcount += 1;
+
+  return parser;
+}
+
+void
+parser_unref (Parser *parser)
+{
+  parser->refcount -= 1;
+  if (parser->refcount == 0)
+    {
+      if (parser->result)
+        node_info_unref (parser->result);
+
+      g_free (parser);
+    }
+}
+
+gboolean
+parser_check_doctype (Parser      *parser,
+                      const char  *doctype,
+                      GError     **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  
+  if (strcmp (doctype, "node") != 0)
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   "D-BUS description file has the wrong document type %s, use node or interface",
+                   doctype);
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+static gboolean
+parse_node (Parser      *parser,
+            const char  *element_name,
+            const char **attribute_names,
+            const char **attribute_values,
+            GError     **error)
+{
+  const char *name;
+  NodeInfo *node;
+  
+  if (parser->interface ||
+      parser->method ||
+      parser->signal ||
+      parser->property ||
+      parser->arg ||
+      parser->in_annotation)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  /* Only the root node can have no name */
+  if (parser->node_stack != NULL && name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  /* Root element name must be absolute */
+  if (parser->node_stack == NULL && name && *name != '/')
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute on <%s> element must be an absolute object path, \"%s\" not OK"),
+                   "name", element_name, name);
+      return FALSE;
+    }
+
+  /* Other element names must not be absolute */
+  if (parser->node_stack != NULL && name && *name == '/')
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute on <%s> element must not be an absolute object path, \"%s\" starts with /"),
+                   "name", element_name, name);
+      return FALSE;
+    }
+  
+  node = node_info_new (name);
+
+  if (parser->node_stack != NULL)
+    {
+      node_info_add_node (parser->node_stack->data,
+                          node);
+    }
+  
+  parser->node_stack = g_slist_prepend (parser->node_stack,
+                                        node);
+  
+  return TRUE;
+}
+
+static gboolean
+parse_interface (Parser      *parser,
+                 const char  *element_name,
+                 const char **attribute_names,
+                 const char **attribute_values,
+                 GError     **error)
+{
+  const char *name;
+  InterfaceInfo *iface;
+  NodeInfo *top;
+  
+  if (parser->interface ||
+      parser->method ||
+      parser->signal ||
+      parser->property ||
+      parser->arg ||
+      parser->in_annotation ||
+      (parser->node_stack == NULL))
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  top = parser->node_stack->data;
+  
+  iface = interface_info_new (name);
+  node_info_add_interface (top, iface);
+  interface_info_unref (iface);
+
+  parser->interface = iface;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_method (Parser      *parser,
+              const char  *element_name,
+              const char **attribute_names,
+              const char **attribute_values,
+              GError     **error)
+{
+  const char *name;
+  MethodInfo *method;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->method ||
+      parser->signal ||
+      parser->property ||
+      parser->in_annotation ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  method = method_info_new (name);
+  interface_info_add_method (parser->interface, method);
+  method_info_unref (method);
+
+  parser->method = method;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_signal (Parser      *parser,
+              const char  *element_name,
+              const char **attribute_names,
+              const char **attribute_values,
+              GError     **error)
+{
+  const char *name;
+  SignalInfo *signal;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->signal ||
+      parser->method ||
+      parser->property ||
+      parser->in_annotation ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  signal = signal_info_new (name);
+  interface_info_add_signal (parser->interface, signal);
+  signal_info_unref (signal);
+
+  parser->signal = signal;
+  
+  return TRUE;
+}
+
+static gboolean
+validate_signature (const char *str,
+		    const char *element_name,
+		    GError    **error)
+{
+  DBusError derror;
+
+  dbus_error_init (&derror);
+  
+  if (!dbus_signature_validate (str, &derror))
+    {
+      dbus_set_g_error (error, &derror);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static gboolean
+parse_property (Parser      *parser,
+                const char  *element_name,
+                const char **attribute_names,
+                const char **attribute_values,
+                GError     **error)
+{
+  const char *name;
+  const char *access;
+  const char *type;
+  PropertyInfo *property;
+  PropertyAccessFlags access_flags;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->signal ||
+      parser->method ||
+      parser->property ||
+      parser->in_annotation ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          "access", &access,
+                          "type", &type,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  if (access == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "access", element_name);
+      return FALSE;
+    }
+
+  if (type == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "type", element_name);
+      return FALSE;
+    }
+
+  if (!validate_signature (type, element_name, error))
+    return FALSE;
+
+  access_flags = 0;
+  if (strcmp (access, "readwrite") == 0)
+    access_flags = PROPERTY_READ | PROPERTY_WRITE;
+  else if (strcmp (access, "read") == 0)
+    access_flags = PROPERTY_READ;
+  else if (strcmp (access, "write") == 0)
+    access_flags = PROPERTY_WRITE;
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("access=\"%s\" must have value readwrite, read, or write on %s\n"),
+                   access, element_name);
+      return FALSE;
+    }
+  
+  property = property_info_new (name, type, access_flags);
+  interface_info_add_property (parser->interface, property);
+  property_info_unref (property);
+
+  parser->property = property;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_arg (Parser      *parser,
+           const char  *element_name,
+           const char **attribute_names,
+           const char **attribute_values,
+           GError     **error)
+{
+  const char *name;
+  const char *type;
+  const char *direction;
+  ArgDirection dir;
+  ArgInfo *arg;
+  char *generated_name;
+  
+  if (!(parser->method || parser->signal) ||
+      parser->node_stack == NULL ||
+      parser->property ||
+      parser->in_annotation ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          "type", &type,
+                          "direction", &direction,
+                          NULL))
+    return FALSE;
+
+  /* name can be null for args */
+  
+  if (type == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "type", element_name);
+      return FALSE;
+    }
+
+  if (direction == NULL)
+    {
+      /* methods default to in, signal to out */
+      if (parser->method)
+        direction = "in";
+      else if (parser->signal)
+        direction = "out";
+      else
+        g_assert_not_reached ();
+    }
+
+  dir = ARG_INVALID;
+  
+  if (strcmp (direction, "in") == 0)
+    dir = ARG_IN;
+  else if (strcmp (direction, "out") == 0)
+    dir = ARG_OUT;
+  
+  if (dir == ARG_INVALID ||
+      (parser->signal && dir == ARG_IN))
+    {
+      if (parser->signal)
+        g_set_error (error, G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_PARSE,
+                     _("Signals must have direction=\"out\" (just omit the direction attribute)"));
+      else
+        g_set_error (error, G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_PARSE,
+                     _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
+                     "direction", element_name);
+      return FALSE;
+    }
+
+  if (!validate_signature (type, element_name, error))
+    return FALSE;
+
+  generated_name = NULL;
+  if (name == NULL)
+    generated_name = g_strdup_printf ("arg%d",
+                                      parser->method ?
+                                      method_info_get_n_args (parser->method) :
+                                      signal_info_get_n_args (parser->signal));
+                                      
+  arg = arg_info_new (name ? name : generated_name, dir, type);
+  if (parser->method)
+    method_info_add_arg (parser->method, arg);
+  else if (parser->signal)
+    signal_info_add_arg (parser->signal, arg);
+  else
+    g_assert_not_reached ();
+
+  g_free (generated_name);
+  
+  arg_info_unref (arg);
+
+  parser->arg = arg;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_annotation (Parser      *parser,
+		  const char  *element_name,
+		  const char **attribute_names,
+		  const char **attribute_values,
+		  GError     **error)
+{
+  const char *name;
+  const char *value;
+  
+  if (!(parser->method || parser->interface || parser->arg) || 
+      parser->node_stack == NULL ||
+      parser->signal ||
+      parser->property ||
+      parser->in_annotation)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          "value", &value,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+  if (value == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "value", element_name);
+      return FALSE;
+    }
+
+  if (parser->arg)
+    arg_info_add_annotation (parser->arg, name, value);
+  else if (parser->method)
+    method_info_add_annotation (parser->method, name, value);
+  else if (parser->interface)
+    interface_info_add_annotation (parser->interface, name, value);
+  else
+    g_assert_not_reached ();
+
+  parser->in_annotation = TRUE;
+
+  return TRUE;
+}
+
+gboolean
+parser_start_element (Parser      *parser,
+                      const char  *element_name,
+                      const char **attribute_names,
+                      const char **attribute_values,
+                      GError     **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (ELEMENT_IS ("node"))
+    {
+      if (!parse_node (parser, element_name, attribute_names,
+                       attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("interface"))
+    {
+      if (!parse_interface (parser, element_name, attribute_names,
+                            attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("method"))
+    {
+      if (!parse_method (parser, element_name, attribute_names,
+                         attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("signal"))
+    {
+      if (!parse_signal (parser, element_name, attribute_names,
+                         attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("property"))
+    {
+      if (!parse_property (parser, element_name, attribute_names,
+                           attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("arg"))
+    {
+      if (!parse_arg (parser, element_name, attribute_names,
+                      attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("annotation"))
+    {
+      if (!parse_annotation (parser, element_name, attribute_names,
+			     attribute_values, error))
+        return FALSE;
+    }
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Element <%s> not recognized"),
+                   element_name);
+    }
+  
+  return TRUE;
+}
+
+gboolean
+parser_end_element (Parser      *parser,
+                    const char  *element_name,
+                    GError     **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (ELEMENT_IS ("interface"))
+    {
+      parser->interface = NULL;
+    }
+  else if (ELEMENT_IS ("method"))
+    {
+      parser->method = NULL;
+    }
+  else if (ELEMENT_IS ("signal"))
+    {
+      parser->signal = NULL;
+    }
+  else if (ELEMENT_IS ("property"))
+    {
+      parser->property = NULL;
+    }
+  else if (ELEMENT_IS ("arg"))
+    {
+      parser->arg = NULL;
+    }
+  else if (ELEMENT_IS ("annotation"))
+    {
+      parser->in_annotation = FALSE;
+    }
+  else if (ELEMENT_IS ("node"))
+    {
+      NodeInfo *top;
+
+      g_assert (parser->node_stack != NULL);
+      top = parser->node_stack->data;
+
+      parser->node_stack = g_slist_remove (parser->node_stack,
+                                           top);
+
+      if (parser->node_stack == NULL)
+        parser->result = top; /* We are done, store the result */      
+    }
+  else
+    g_assert_not_reached (); /* should have had an error on start_element */
+  
+  return TRUE;
+}
+
+gboolean
+parser_content (Parser      *parser,
+                const char  *content,
+                int          len,
+                GError     **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  /* FIXME check that it's all whitespace */
+  
+  return TRUE;
+}
+
+gboolean
+parser_finished (Parser      *parser,
+                 GError     **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  return TRUE;
+}
+
+NodeInfo*
+parser_get_nodes (Parser *parser)
+{
+  return parser->result;
+}
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */