[gconf] Add a data conversion utility



commit 9a6d45c235a30342f3c679a09373cc5dc67acc2a
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Apr 16 20:19:00 2010 -0400

    Add a data conversion utility

 gsettings/Makefile.am                |   29 +++
 gsettings/gsettings-data-convert.c   |  435 ++++++++++++++++++++++++++++++++++
 gsettings/gsettings-data-convert.xml |   88 +++++++
 3 files changed, 552 insertions(+), 0 deletions(-)
---
diff --git a/gsettings/Makefile.am b/gsettings/Makefile.am
index 74c69fc..8135d2a 100644
--- a/gsettings/Makefile.am
+++ b/gsettings/Makefile.am
@@ -22,7 +22,36 @@ libgsettingsgconfbackend_la_LIBADD =        \
 	$(GSETTINGS_LIBS)                   \
 	$(NULL)
 
+bin_PROGRAMS = gsettings-data-convert
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)         \
+	-I$(top_builddir)       \
+	$(GSETTINGS_CFLAGS)
+
+gsettings_data_convert_SOURCES = gsettings-data-convert.c
+gsettings_data_convert_LDADD =              \
+	$(top_builddir)/gconf/libgconf-2.la \
+	$(GSETTINGS_LIBS)                   \
+	$(NULL)
+
+man_MANS =                       \
+	gsettings-data-convert.1 \
+	$(NULL)
+
+gsettings-data-convert.1 : gsettings-data-convert.xml
+	xsltproc -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+BUILT_EXTRA_DIST = $(man_MANS)
+
+dist-hook-local: $(BUILT_EXTRA_DIST)
+	files='$(BUILT_EXTRA_DIST)';                      \
+	for f in $$files; do                              \
+	  if test -f $$f; then d=.; else d=$(srcdir); fi; \
+	  cp $$d/$$f $(distdir) || exit 1; done
+
 install-data-hook:
 	if test -z "$(DESTDIR)" -a "$(GIO_QUERYMODULES)" != "no" ; then                         \
 		$(GIO_QUERYMODULES) $(GIO_MODULE_DIR) ;                                         \
 	fi
+
diff --git a/gsettings/gsettings-data-convert.c b/gsettings/gsettings-data-convert.c
new file mode 100644
index 0000000..c7fb866
--- /dev/null
+++ b/gsettings/gsettings-data-convert.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * Author: Matthias Clasen <mclasen redhat com>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gconf/gconf-client.h>
+
+static const gchar convert_dir[] = "/usr/share/gsettings-data-convert";
+
+static gboolean verbose = FALSE;
+static gboolean dry_run = FALSE;
+
+static gboolean
+handle_file (const gchar *filename)
+{
+  GKeyFile *keyfile;
+  GConfClient *client;
+  GConfValue *value;
+  gint i, j;
+  gchar *gconf_key;
+  gchar **groups;
+  gchar **keys;
+  GConfValueType list_type;
+  GVariantBuilder *builder;
+  GVariant *v;
+  const gchar *s;
+  gchar *str;
+  gint ii;
+  GSList *list, *l;
+  GSettings *settings;
+  GError *error;
+
+  keyfile = g_key_file_new ();
+
+  error = NULL;
+  if (!g_key_file_load_from_file (keyfile, filename, 0, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+
+      g_key_file_free (keyfile);
+
+      return FALSE;
+    }
+
+  client = gconf_client_get_default ();
+
+  groups = g_key_file_get_groups (keyfile, NULL);
+  for (i = 0; groups[i]; i++)
+    {
+      if (verbose)
+        g_print ("collecting settings for schema '%s'\n", groups[i]);
+
+      settings = g_settings_new (groups[i]);
+      g_settings_delay (settings);
+
+      error = NULL;
+      if ((keys = g_key_file_get_keys (keyfile, groups[i], NULL, &error)) == NULL)
+        {
+          g_printerr ("%s", error->message);
+          g_error_free (error);
+
+          continue;
+        }
+
+      for (j = 0; keys[j]; j++)
+        {
+          error = NULL;
+          if ((gconf_key = g_key_file_get_string (keyfile, groups[i], keys[j], &error)) ==  NULL)
+            {
+              g_printerr ("%s", error->message);
+              g_error_free (error);
+
+              continue;
+            }
+
+          error = NULL;
+          if ((value = gconf_client_get_without_default (client, gconf_key, &error)) == NULL)
+            {
+              if (error)
+                {
+                  g_printerr ("Failed to get GConf key '%s': %s\n", gconf_key, error->message);
+                  g_error_free (error);
+                }
+              else
+                {
+                  if (verbose)
+                    g_print ("Skipping GConf key '%s', no user value\n", gconf_key);
+                }
+
+              g_free (gconf_key);
+
+              continue;
+            }
+
+          switch (value->type)
+            {
+            case GCONF_VALUE_STRING:
+              if (dry_run)
+                g_print ("set key '%s' to string '%s'\n", keys[j], gconf_value_get_string (value));
+              else
+                g_settings_set (settings, keys[j], "s", gconf_value_get_string (value));
+              break;
+
+            case GCONF_VALUE_INT:
+              if (dry_run)
+                g_print ("set key '%s' to integer '%d'\n", keys[j], gconf_value_get_int (value));
+              else
+                g_settings_set (settings, keys[j], "i", gconf_value_get_int (value));
+              break;
+
+            case GCONF_VALUE_BOOL:
+              if (dry_run)
+                g_print ("set key '%s' to boolean '%d'\n", keys[j], gconf_value_get_bool (value));
+              else
+                g_settings_set (settings, keys[j], "b", gconf_value_get_bool (value));
+              break;
+
+            case GCONF_VALUE_FLOAT:
+              if (dry_run)
+                g_print ("set key '%s' to double '%g'\n", keys[j], gconf_value_get_float (value));
+              else
+                g_settings_set (settings, keys[j], "d", gconf_value_get_float (value));
+              break;
+
+            case GCONF_VALUE_LIST:
+              switch (gconf_value_get_list_type (value))
+                {
+                case GCONF_VALUE_STRING:
+                  builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+                  list = gconf_value_get_list (value);
+                  for (l = list; l; l = l->next)
+                    {
+                      GConfValue *lv = l->data;
+                      s = gconf_value_get_string (lv);
+                      g_variant_builder_add (builder, "s", s);
+                    }
+                  v = g_variant_new ("as", builder);
+
+                  if (dry_run)
+                    {
+                      str = g_variant_print (v, FALSE);
+                      g_print ("set key '%s' to a list of strings: %s\n", keys[j], str);
+                      g_free (str);
+                    }
+                  else
+                    g_settings_set_value (settings, keys[j], v);
+
+                  g_variant_unref (v);
+                  g_variant_builder_unref (builder);
+                  break;
+
+                case GCONF_VALUE_INT:
+                  builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
+                  list = gconf_value_get_list (value);
+                  for (l = list; l; l = l->next)
+                    {
+                      GConfValue *lv = l->data;
+                      ii = gconf_value_get_int (lv);
+                      g_variant_builder_add (builder, "i", ii);
+                    }
+                  v = g_variant_new ("ai", builder);
+
+                  if (dry_run)
+                    {
+                      str = g_variant_print (v, FALSE);
+                      g_print ("set key '%s' to a list of integers: %s\n", keys[j], str);
+                      g_free (str);
+                    }
+                  else
+                    g_settings_set_value (settings, keys[j], v);
+
+                  g_variant_unref (v);
+                  g_variant_builder_unref (builder);
+                  break;
+
+                default:
+                  g_printerr ("Keys of type 'list of %s' not handled yet\n", gconf_value_type_to_string (gconf_value_get_list_type (value)));
+                  break;
+                }
+              break;
+
+            default:
+              g_printerr ("Keys of type %s not handled yet\n", gconf_value_type_to_string (value->type));
+              break;
+            }
+
+          gconf_value_free (value);
+          g_free (gconf_key);
+        }
+
+      g_strfreev (keys);
+
+      if (!dry_run)
+        g_settings_apply (settings);
+
+      g_object_unref (settings);
+    }
+
+  g_strfreev (groups);
+
+  g_object_unref (client);
+
+  return TRUE;
+}
+
+static void
+load_state (time_t  *mtime,
+            gchar ***converted)
+{
+  gchar *filename;
+  GKeyFile *keyfile;
+  GError *error;
+  gchar *str;
+  gchar **list;
+
+  *mtime = 0;
+  *converted = g_new0 (gchar *, 1);
+
+  filename = g_build_filename (g_get_user_data_dir (), "gsettings-data-convert", NULL);
+  keyfile = g_key_file_new ();
+
+  error = NULL;
+  if (!g_key_file_load_from_file (keyfile, filename, 0, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  error = NULL;
+  if ((str = g_key_file_get_string (keyfile, "State", "timestamp", &error)) == NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+    }
+  else
+    {
+      *mtime = (time_t)g_ascii_strtoll (str, NULL, 0);
+      g_free (str);
+    }
+
+  error = NULL;
+  if ((list = g_key_file_get_string_list (keyfile, "State", "converted", NULL, &error)) == NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+    }
+  else
+    {
+      g_strfreev (*converted);
+      *converted = list;
+    }
+
+  g_key_file_free (keyfile);
+  g_free (filename);
+}
+
+static gboolean
+save_state (time_t   mtime,
+            gchar  **converted)
+{
+  gchar *filename;
+  GKeyFile *keyfile;
+  gchar *str;
+  GError *error;
+  gboolean result;
+
+  /* Make sure the state directory exists */
+  if (g_mkdir_with_parents (g_get_user_data_dir (), 0755))
+    {
+      g_printerr ("Failed to create directory %s: %s\n",
+                  g_get_user_data_dir (), g_strerror (errno));
+      return FALSE;
+    }
+
+  filename = g_build_filename (g_get_user_data_dir (), "gsettings-data-convert", NULL);
+  keyfile = g_key_file_new ();
+
+  str = g_strdup_printf ("%ld", mtime);
+  g_key_file_set_string (keyfile,
+                         "State", "timestamp", str);
+  g_free (str);
+
+  g_key_file_set_string_list (keyfile,
+                              "State", "converted",
+                              (const gchar * const *)converted, g_strv_length (converted));
+
+  str = g_key_file_to_data (keyfile, NULL, NULL);
+  g_key_file_free (keyfile);
+
+  error = NULL;
+  if (!g_file_set_contents (filename, str, -1, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+
+      result = FALSE;
+    }
+  else
+    result = TRUE;
+
+  g_free (filename);
+  g_free (str);
+
+  return result;
+}
+
+int
+main (int argc, char *argv[])
+{
+  gchar *state_filename;
+  time_t stored_mtime;
+  time_t dir_mtime;
+  struct stat statbuf;
+  gchar *contents;
+  GError *error;
+  gchar *converted_filename;
+  gchar **converted;
+  GConfClient *client;
+  GDir *dir;
+  const gchar *name;
+  gchar *filename;
+  GString *string;
+  gint i;
+  GOptionContext *context;
+  GOptionEntry entries[] = {
+    { "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose, "show verbose messages", NULL },
+    { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, "do not perform any changes", NULL },
+    { NULL }
+  };
+
+  context = g_option_context_new ("");
+
+  g_option_context_set_summary (context,
+    "Migrate settings from the users GConf database to GSettings.");
+
+  g_option_context_add_main_entries (context, entries, NULL);
+
+  error = NULL;
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return 1;
+    }
+
+  load_state (&stored_mtime, &converted);
+
+  /* If the directory is not newer, exit */
+  if (stat (convert_dir, &statbuf) == 0)
+    dir_mtime = statbuf.st_mtime;
+ else
+    {
+      if (verbose)
+        g_print ("Directory '%s' does not exist, nothing to do\n", convert_dir);
+      return 0;
+    }
+
+  if (dir_mtime <= stored_mtime)
+    {
+      if (verbose)
+        g_print ("All uptodate, nothing to do\n");
+      return 0;
+    }
+
+  error = NULL;
+  dir = g_dir_open (convert_dir, 0, &error);
+  if (dir == NULL)
+    {
+      g_printerr ("Failed to open '%s': %s\n", error->message);
+      return 1;
+    }
+
+  while ((name = g_dir_read_name (dir)) != NULL)
+    {
+       for (i = 0; converted[i]; i++)
+         {
+           if (strcmp (name, converted[i]) == 0)
+             {
+               if (verbose)
+                 g_print ("File '%s already converted, skipping\n", name);
+               goto next;
+             }
+         }
+
+      filename = g_build_filename (convert_dir, name, NULL);
+
+      if (handle_file (filename))
+        {
+          gint len;
+
+          /* Add the the file to the converted list */
+          len = g_strv_length (converted);
+          converted = g_realloc (converted, len + 1);
+          converted[len] = g_strdup (name);
+          converted[len + 1] = NULL;
+        }
+
+      g_free (filename);
+
+ next: ;
+    }
+
+  if (!dry_run)
+    {
+      if (!save_state (dir_mtime, converted))
+        return 1;
+    }
+
+  return 0;
+}
+
diff --git a/gsettings/gsettings-data-convert.xml b/gsettings/gsettings-data-convert.xml
new file mode 100644
index 0000000..c45cdc8
--- /dev/null
+++ b/gsettings/gsettings-data-convert.xml
@@ -0,0 +1,88 @@
+<refentry id="gsettings-data-convert">
+
+<refmeta>
+<refentrytitle>gsettings-data-convert</refentrytitle>
+<manvolnum>1</manvolnum>
+<refmiscinfo class="manual">User Commands</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>gsettings-data-convert</refname>
+<refpurpose>GConf to GSettings data migration</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+<cmdsynopsis>
+<command>gsettings-data-convert</command>
+<arg choice="opt" rep="repeat">option</arg>
+</cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1><title>Description</title>
+<para>
+<command>gsettings-data-convert</command> reads values out of the users
+GConf database and stores them in GSettings.
+</para>
+<para>
+The information about the mapping from GConf keys to GSettings keys
+is taken from files in <filename>/usr/share/gconv-convert</filename>.
+Each file in that directory is read as a key file, with sections
+for each GSettings schema that is being converted. The entries in
+each section map GSettings keys to paths in the GConf database.
+Currently, <command>gsettings-data-convert</command> supports all
+basic GConf types as well as lists of strings and lists of integers.
+</para>
+<para>
+A simple example might look like this:
+<programlisting>
+[org.gnome.fonts]
+antialiasing = /desktop/gnome/font_rendering/antialiasing
+dpi = /desktop/gnome/font_rendering/dpi
+hinting = /desktop/gnome/font_rendering/hinting
+rgba_order = /desktop/gnome/font_rendering/rgba_order
+</programlisting>
+</para>
+<para>
+<command>gsettings-data-convert</command> keeps a list of the key files it
+has already converted, so it is safe to run it repeatedly to handle
+newly appeared key files. The expected use of this utility is to make
+each application install a key file for the GConf keys that it
+wants to be migrated, and run <command>gsettings-data-convert</command>
+every time a user logs in.
+</para>
+
+<refsect2><title>Options</title>
+<variablelist>
+
+<varlistentry>
+<term><option>-h</option>, <option>--help</option></term>
+<listitem><para>
+Print help and exit
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--dry-run</option></term>
+<listitem><para>
+Do not make any changes, just report what would be done
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--verbose</option></term>
+<listitem><para>
+Show verbose messages
+</para></listitem>
+</varlistentry>
+
+</variablelist>
+</refsect2>
+</refsect1>
+<refsect1><title>See also</title>
+<para>
+<command>gsettings-schema-convert(1)</command> is a related command that
+helps with the conversion of schemas from GConf to GSettings.
+</para>
+</refsect1>
+</refentry>
+



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