[gnome-desktop] Revert "xkbinfo: use libxkbregistry to parse the rules files for us"



commit a8c94b74a8182c48eb5cd5e2cc9b03d5ee52d4bd
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Thu Sep 24 22:50:50 2020 +0000

    Revert "xkbinfo: use libxkbregistry to parse the rules files for us"
    
    This reverts commit 4f6bec60bfc781c59d5afb6f968fc94ad859e5b9

 .gitlab-ci.yml                    |   2 +-
 libgnome-desktop/gnome-xkb-info.c | 485 +++++++++++++++++++++++++++++---------
 libgnome-desktop/meson.build      |   1 -
 meson.build                       |   1 -
 4 files changed, 370 insertions(+), 119 deletions(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6fe953d9..b1a5d8e4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,7 +3,7 @@ image: fedora:rawhide
 variables:
   LAST_ABI_BREAK: 9d01763ba2a3f71b7c0aade04d2ffa6a883e308d
   DEPENDENCIES: gtk3-devel gsettings-desktop-schemas-devel gettext
-                gtk-doc libxkbcommon-devel xkeyboard-config-devel itstool
+                gtk-doc xkeyboard-config-devel itstool
                 gobject-introspection-devel systemd-devel iso-codes-devel
                 libseccomp-devel gcc gcc-c++ glibc-devel
                 meson redhat-rpm-config
diff --git a/libgnome-desktop/gnome-xkb-info.c b/libgnome-desktop/gnome-xkb-info.c
index 178c6580..f2f51abb 100644
--- a/libgnome-desktop/gnome-xkb-info.c
+++ b/libgnome-desktop/gnome-xkb-info.c
@@ -21,8 +21,6 @@
 
 #include <config.h>
 
-#include <xkbcommon/xkbregistry.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -75,6 +73,15 @@ struct _GnomeXkbInfoPrivate
   GHashTable *layouts_by_country;
   GHashTable *layouts_by_language;
   GHashTable *layouts_table;
+
+  /* Only used while parsing */
+  XkbOptionGroup *current_parser_group;
+  XkbOption *current_parser_option;
+  Layout *current_parser_layout;
+  Layout *current_parser_variant;
+  gchar  *current_parser_iso639Id;
+  gchar  *current_parser_iso3166Id;
+  gchar **current_parser_text;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT,
@@ -121,6 +128,164 @@ free_option_group (gpointer data)
   g_slice_free (XkbOptionGroup, group);
 }
 
+static gchar *
+get_xml_rules_file_path (const gchar *suffix)
+{
+  const gchar *base_path;
+  gchar *rules_file;
+  gchar *xml_rules_file;
+
+  base_path = g_getenv ("XKB_CONFIG_ROOT");
+  if (!base_path)
+    base_path = XKB_BASE;
+
+  rules_file = g_build_filename (base_path, "rules", XKB_RULES_FILE, NULL);
+  xml_rules_file = g_strdup_printf ("%s%s", rules_file, suffix);
+  g_free (rules_file);
+
+  return xml_rules_file;
+}
+
+static void
+parse_start_element (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     const gchar         **attribute_names,
+                     const gchar         **attribute_values,
+                     gpointer              data,
+                     GError              **error)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  if (priv->current_parser_text)
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Expected character data but got element '%s'", element_name);
+      return;
+    }
+
+  if (strcmp (element_name, "name") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->xkb_name;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->xkb_name;
+      else if (priv->current_parser_option)
+        priv->current_parser_text = &priv->current_parser_option->id;
+      else if (priv->current_parser_group)
+        priv->current_parser_text = &priv->current_parser_group->id;
+    }
+  else if (strcmp (element_name, "description") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->description;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->description;
+      else if (priv->current_parser_option)
+        priv->current_parser_text = &priv->current_parser_option->description;
+      else if (priv->current_parser_group)
+        priv->current_parser_text = &priv->current_parser_group->description;
+    }
+  else if (strcmp (element_name, "shortDescription") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->short_desc;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->short_desc;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      priv->current_parser_text = &priv->current_parser_iso639Id;
+    }
+  else if (strcmp (element_name, "iso3166Id") == 0)
+    {
+      priv->current_parser_text = &priv->current_parser_iso3166Id;
+    }
+  else if (strcmp (element_name, "layout") == 0)
+    {
+      if (priv->current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements can't be nested");
+          return;
+        }
+
+      priv->current_parser_layout = g_slice_new0 (Layout);
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      Layout *layout;
+
+      if (priv->current_parser_variant)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements can't be nested");
+          return;
+        }
+
+      if (!priv->current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must be inside 'layout' elements");
+          return;
+        }
+
+      if (!priv->current_parser_layout->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must be inside named 'layout' elements");
+          return;
+        }
+
+      layout = g_hash_table_lookup (priv->layouts_table, priv->current_parser_layout->xkb_name);
+      if (!layout)
+        layout = priv->current_parser_layout;
+
+      priv->current_parser_variant = g_slice_new0 (Layout);
+      priv->current_parser_variant->is_variant = TRUE;
+      priv->current_parser_variant->main_layout = layout;
+    }
+  else if (strcmp (element_name, "group") == 0)
+    {
+      if (priv->current_parser_group)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'group' elements can't be nested");
+          return;
+        }
+
+      priv->current_parser_group = g_slice_new0 (XkbOptionGroup);
+      /* Maps option ids to XkbOption structs. Owns the XkbOption structs. */
+      priv->current_parser_group->options_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                                         NULL, free_option);
+      g_markup_collect_attributes (element_name,
+                                   attribute_names,
+                                   attribute_values,
+                                   error,
+                                   G_MARKUP_COLLECT_BOOLEAN | G_MARKUP_COLLECT_OPTIONAL,
+                                   "allowMultipleSelection",
+                                   &priv->current_parser_group->allow_multiple_selection,
+                                   G_MARKUP_COLLECT_INVALID);
+    }
+  else if (strcmp (element_name, "option") == 0)
+    {
+      if (priv->current_parser_option)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'option' elements can't be nested");
+          return;
+        }
+
+      if (!priv->current_parser_group)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'option' elements must be inside 'group' elements");
+          return;
+        }
+
+      priv->current_parser_option = g_slice_new0 (XkbOption);
+    }
+}
+
 static void
 add_layout_to_table (GHashTable  *table,
                      const gchar *key,
@@ -185,136 +350,205 @@ add_layout_to_locale_tables (Layout     *layout,
     }
 }
 
-enum layout_subsets {
-    ONLY_MAIN_LAYOUTS,
-    ONLY_VARIANTS,
-};
+static void
+add_iso639 (Layout *layout,
+            gchar  *id)
+{
+  layout->iso639Ids = g_slist_prepend (layout->iso639Ids, id);
+}
 
 static void
-add_layouts (GnomeXkbInfo  *self, struct rxkb_context *ctx,
-            enum layout_subsets which)
+add_iso3166 (Layout *layout,
+             gchar  *id)
 {
-  GnomeXkbInfoPrivate *priv = self->priv;
-  struct rxkb_layout *layout;
+  layout->iso3166Ids = g_slist_prepend (layout->iso3166Ids, id);
+}
+
+static void
+parse_end_element (GMarkupParseContext  *context,
+                   const gchar          *element_name,
+                   gpointer              data,
+                   GError              **error)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
 
-  for (layout = rxkb_layout_first (ctx);
-       layout;
-       layout = rxkb_layout_next (layout))
+  if (strcmp (element_name, "layout") == 0)
     {
-      struct rxkb_iso639_code *iso639;
-      struct rxkb_iso3166_code *iso3166;
-      const char *name, *variant;
-      Layout *l;
+      if (!priv->current_parser_layout->description || !priv->current_parser_layout->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements must enclose 'description' and 'name' elements");
+          return;
+        }
 
-      name = rxkb_layout_get_name (layout);
-      variant = rxkb_layout_get_variant (layout);
+      priv->current_parser_layout->id = g_strdup (priv->current_parser_layout->xkb_name);
 
-      if ((which == ONLY_VARIANTS && variant == NULL) ||
-          (which == ONLY_MAIN_LAYOUTS && variant != NULL))
-          continue;
+      if (g_hash_table_contains (priv->layouts_table, priv->current_parser_layout->id))
+        {
+          g_clear_pointer (&priv->current_parser_layout, free_layout);
+          return;
+        }
 
-      l = g_slice_new0 (Layout);
-      if (variant)
+      g_hash_table_replace (priv->layouts_table,
+                            priv->current_parser_layout->id,
+                            priv->current_parser_layout);
+      add_layout_to_locale_tables (priv->current_parser_layout,
+                                   priv->layouts_by_language,
+                                   priv->layouts_by_country);
+      priv->current_parser_layout = NULL;
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      if (!priv->current_parser_variant->description || !priv->current_parser_variant->xkb_name)
         {
-          /* This relies on the main layouts being added first */
-          l->main_layout = g_hash_table_lookup (priv->layouts_table, name);
-          if (l->main_layout == NULL)
-           {
-               /* This is a bug in libxkbregistry */
-               g_warning ("Ignoring variant '%s(%s)' without a main layout",
-                          name, variant);
-               g_free (l);
-               continue;
-           }
-
-          l->xkb_name = g_strdup (variant);
-          l->is_variant = TRUE;
-          l->id = g_strjoin ("+", name, variant, NULL);
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must enclose 'description' and 'name' elements");
+          return;
         }
-      else
+
+      priv->current_parser_variant->id = g_strjoin ("+",
+                                                    priv->current_parser_layout->xkb_name,
+                                                    priv->current_parser_variant->xkb_name,
+                                                    NULL);
+
+      if (g_hash_table_contains (priv->layouts_table, priv->current_parser_variant->id))
         {
-          l->xkb_name = g_strdup (name);
-          l->id = g_strdup (name);
+          g_clear_pointer (&priv->current_parser_variant, free_layout);
+          return;
         }
-      l->description = g_strdup (rxkb_layout_get_description (layout));
-      l->short_desc = g_strdup (rxkb_layout_get_brief (layout));
-      for (iso639 = rxkb_layout_get_iso639_first (layout);
-           iso639;
-           iso639 = rxkb_iso639_code_next (iso639))
+
+      g_hash_table_replace (priv->layouts_table,
+                            priv->current_parser_variant->id,
+                            priv->current_parser_variant);
+      add_layout_to_locale_tables (priv->current_parser_variant,
+                                   priv->layouts_by_language,
+                                   priv->layouts_by_country);
+      priv->current_parser_variant = NULL;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      if (!priv->current_parser_iso639Id)
         {
-          char *id = g_strdup (rxkb_iso639_code_get_code (iso639));
-          l->iso3166Ids = g_slist_prepend (l->iso3166Ids, id);
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'iso639Id' elements must enclose text");
+          return;
         }
-      for (iso3166 = rxkb_layout_get_iso3166_first (layout);
-           iso3166;
-           iso3166 = rxkb_iso3166_code_next (iso3166))
+
+      if (priv->current_parser_variant)
+        add_iso639 (priv->current_parser_variant, priv->current_parser_iso639Id);
+      else if (priv->current_parser_layout)
+        add_iso639 (priv->current_parser_layout, priv->current_parser_iso639Id);
+
+      priv->current_parser_iso639Id = NULL;
+    }
+  else if (strcmp (element_name, "iso3166Id") == 0)
+    {
+      if (!priv->current_parser_iso3166Id)
         {
-          char *id = g_strdup (rxkb_iso3166_code_get_code (iso3166));
-          l->iso3166Ids = g_slist_prepend (l->iso3166Ids, id);
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'iso3166Id' elements must enclose text");
+          return;
         }
 
-      g_hash_table_replace (priv->layouts_table, l->id, l);
-      add_layout_to_locale_tables (l,
-                                   priv->layouts_by_language,
-                                   priv->layouts_by_country);
+      if (priv->current_parser_variant)
+        add_iso3166 (priv->current_parser_variant, priv->current_parser_iso3166Id);
+      else if (priv->current_parser_layout)
+        add_iso3166 (priv->current_parser_layout, priv->current_parser_iso3166Id);
+
+      priv->current_parser_iso3166Id = NULL;
+    }
+  else if (strcmp (element_name, "group") == 0)
+    {
+      if (!priv->current_parser_group->description || !priv->current_parser_group->id)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'group' elements must enclose 'description' and 'name' elements");
+          return;
+        }
+
+      g_hash_table_replace (priv->option_groups_table,
+                            priv->current_parser_group->id,
+                            priv->current_parser_group);
+      priv->current_parser_group = NULL;
+    }
+  else if (strcmp (element_name, "option") == 0)
+    {
+      if (!priv->current_parser_option->description || !priv->current_parser_option->id)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'option' elements must enclose 'description' and 'name' elements");
+          return;
+        }
 
-   }
+      g_hash_table_replace (priv->current_parser_group->options_table,
+                            priv->current_parser_option->id,
+                            priv->current_parser_option);
+      priv->current_parser_option = NULL;
+    }
 }
-static bool
-parse_rules_file (GnomeXkbInfo  *self, const char *ruleset,
-                  bool include_extras)
+
+static void
+parse_text (GMarkupParseContext  *context,
+            const gchar          *text,
+            gsize                 text_len,
+            gpointer              data,
+            GError              **error)
 {
-  GnomeXkbInfoPrivate *priv = self->priv;
-  struct rxkb_context *ctx;
-  struct rxkb_option_group *group;
-  enum rxkb_context_flags flags = RXKB_CONTEXT_NO_FLAGS;
-
-  if (include_extras)
-      flags |= RXKB_CONTEXT_LOAD_EXOTIC_RULES;
-
-  ctx = rxkb_context_new (flags);
-  if (!rxkb_context_parse (ctx, ruleset)) {
-      rxkb_context_unref (ctx);
-      return FALSE;
-  }
-
-  /* libxkbregistry doesn't guarantee a sorting order of the layouts but we
-   * want to reference the main layout from the variants. So populate with
-   * the main layouts first, then add the variants */
-  add_layouts (self, ctx, ONLY_MAIN_LAYOUTS);
-  add_layouts (self, ctx, ONLY_VARIANTS);
-
-  for (group = rxkb_option_group_first (ctx);
-       group;
-       group = rxkb_option_group_next (group))
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  if (priv->current_parser_text)
     {
-        XkbOptionGroup *g;
-        struct rxkb_option *option;
-
-        g = g_slice_new (XkbOptionGroup);
-        g->id = g_strdup (rxkb_option_group_get_name (group));
-        g->description = g_strdup (rxkb_option_group_get_description (group));
-        g->options_table = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                  NULL, free_option);
-        g->allow_multiple_selection = rxkb_option_group_allows_multiple (group);
-        g_hash_table_replace (priv->option_groups_table, g->id, g);
-
-        for (option = rxkb_option_first (group);
-             option;
-             option = rxkb_option_next (option))
-          {
-            XkbOption *o;
-
-            o = g_slice_new (XkbOption);
-            o->id = g_strdup (rxkb_option_get_name (option));
-            o->description = g_strdup(rxkb_option_get_description (option));
-            g_hash_table_replace (g->options_table, o->id, o);
-          }
+      *priv->current_parser_text = g_strndup (text, text_len);
+      priv->current_parser_text = NULL;
     }
+}
+
+static void
+parse_error (GMarkupParseContext *context,
+             GError              *error,
+             gpointer             data)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  free_option_group (priv->current_parser_group);
+  free_option (priv->current_parser_option);
+  free_layout (priv->current_parser_layout);
+  free_layout (priv->current_parser_variant);
+  g_free (priv->current_parser_iso639Id);
+  g_free (priv->current_parser_iso3166Id);
+}
 
-  rxkb_context_unref (ctx);
+static const GMarkupParser markup_parser = {
+  parse_start_element,
+  parse_end_element,
+  parse_text,
+  NULL,
+  parse_error
+};
 
-  return TRUE;
+static void
+parse_rules_file (GnomeXkbInfo  *self,
+                  const gchar   *path,
+                  GError       **error)
+{
+  gchar *buffer;
+  gsize length;
+  GMarkupParseContext *context;
+  GError *sub_error = NULL;
+
+  g_file_get_contents (path, &buffer, &length, &sub_error);
+  if (sub_error)
+    {
+      g_propagate_error (error, sub_error);
+      return;
+    }
+
+  context = g_markup_parse_context_new (&markup_parser, 0, self, NULL);
+  g_markup_parse_context_parse (context, buffer, length, &sub_error);
+  g_markup_parse_context_free (context);
+  g_free (buffer);
+  if (sub_error)
+    g_propagate_error (error, sub_error);
 }
 
 static void
@@ -323,6 +557,8 @@ parse_rules (GnomeXkbInfo *self)
   GnomeXkbInfoPrivate *priv = self->priv;
   GSettings *settings;
   gboolean show_all_sources;
+  gchar *file_path;
+  GError *error = NULL;
 
   /* Make sure the translated strings we get from XKEYBOARD_CONFIG() are
    * in UTF-8 and not in the current locale */
@@ -345,18 +581,35 @@ parse_rules (GnomeXkbInfo *self)
   /* Maps layout ids to Layout structs. Owns the Layout structs. */
   priv->layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout);
 
+  file_path = get_xml_rules_file_path (".xml");
+  parse_rules_file (self, file_path, &error);
+  if (error)
+    goto cleanup;
+  g_free (file_path);
+
   settings = g_settings_new ("org.gnome.desktop.input-sources");
   show_all_sources = g_settings_get_boolean (settings, "show-all-sources");
   g_object_unref (settings);
 
-  if (!parse_rules_file (self, XKB_RULES_FILE, show_all_sources))
-    {
-      g_warning ("Failed to load '%s' XKB layouts", XKB_RULES_FILE);
-      g_clear_pointer (&priv->option_groups_table, g_hash_table_destroy);
-      g_clear_pointer (&priv->layouts_by_country, g_hash_table_destroy);
-      g_clear_pointer (&priv->layouts_by_language, g_hash_table_destroy);
-      g_clear_pointer (&priv->layouts_table, g_hash_table_destroy);
-    }
+  if (!show_all_sources)
+    return;
+
+  file_path = get_xml_rules_file_path (".extras.xml");
+  parse_rules_file (self, file_path, &error);
+  if (error)
+    goto cleanup;
+  g_free (file_path);
+
+  return;
+
+ cleanup:
+  g_warning ("Failed to load XKB rules file %s: %s", file_path, error->message);
+  g_clear_pointer (&error, g_error_free);
+  g_clear_pointer (&file_path, g_free);
+  g_clear_pointer (&priv->option_groups_table, g_hash_table_destroy);
+  g_clear_pointer (&priv->layouts_by_country, g_hash_table_destroy);
+  g_clear_pointer (&priv->layouts_by_language, g_hash_table_destroy);
+  g_clear_pointer (&priv->layouts_table, g_hash_table_destroy);
 }
 
 static gboolean
diff --git a/libgnome-desktop/meson.build b/libgnome-desktop/meson.build
index b10a18df..ca1e4b2a 100644
--- a/libgnome-desktop/meson.build
+++ b/libgnome-desktop/meson.build
@@ -80,7 +80,6 @@ gnome_desktop_deps = [
   libsystemd_dep,
   schemas_dep,
   xkb_config_dep,
-  xkbregistry_dep,
   iso_codes_dep,
   udev_dep,
   seccomp_dep
diff --git a/meson.build b/meson.build
index 51223492..7ea72f71 100644
--- a/meson.build
+++ b/meson.build
@@ -47,7 +47,6 @@ gio_unix_dep = dependency('gio-unix-2.0', version: glib_req)
 schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
 fontconfig_dep = dependency('fontconfig')
 xkb_config_dep = dependency('xkeyboard-config')
-xkbregistry_dep = dependency('xkbregistry')
 iso_codes_dep = dependency('iso-codes')
 
 libsystemd_dep = dependency('libsystemd', required: get_option('systemd'))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]