[gimp/soc-2011-seamless-clone2] config: migration infrastructure to update configuration files.



commit eca10c3aecb0a688c6f8c0d01591b40e6015acd3
Author: Jehan <jehan girinstud io>
Date:   Wed Dec 12 15:30:31 2012 +0900

    config: migration infrastructure to update configuration files.
    
    This is a generic system based off regular expressions so it can be used
    for any configuration file.
    Some of the use cases would be for instance to clean out outdated custom
    actions (hence remove some loading burden), or rename them (so that
    users don't lose their customization if we rename actions), etc.

 app/config/gimpconfig-file.c |   91 +++++++++++++++++++++++++++++++++++++++--
 app/config/gimpconfig-file.h |    6 ++-
 app/core/gimp-user-install.c |   67 +++++++++++++++++++++++++------
 app/menus/menus.c            |    2 +-
 4 files changed, 145 insertions(+), 21 deletions(-)
---
diff --git a/app/config/gimpconfig-file.c b/app/config/gimpconfig-file.c
index 700a7c8..069428e 100644
--- a/app/config/gimpconfig-file.c
+++ b/app/config/gimpconfig-file.c
@@ -41,8 +41,10 @@
 
 
 gboolean
-gimp_config_file_copy (const gchar  *source,
-                       const gchar  *dest,
+gimp_config_file_copy (const gchar        *source,
+                       const gchar        *dest,
+                       const gchar        *old_options_pattern,
+                       GRegexEvalCallback  update_callback,
                        GError      **error)
 {
   gchar        buffer[8192];
@@ -50,6 +52,12 @@ gimp_config_file_copy (const gchar  *source,
   FILE        *dfile;
   struct stat  stat_buf;
   gint         nbytes;
+  gint         unwritten_len = 0;
+  GRegex      *old_options_regexp = NULL;
+
+  if (old_options_pattern && update_callback && !(old_options_regexp = g_regex_new (old_options_pattern, 0, 
0, error)))
+    /* error set by g_regex_new. */
+    return FALSE;
 
   sfile = g_fopen (source, "rb");
   if (sfile == NULL)
@@ -57,6 +65,8 @@ gimp_config_file_copy (const gchar  *source,
       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                    _("Could not open '%s' for reading: %s"),
                    gimp_filename_to_utf8 (source), g_strerror (errno));
+      if (old_options_regexp)
+        g_regex_unref (old_options_regexp);
       return FALSE;
     }
 
@@ -67,20 +77,85 @@ gimp_config_file_copy (const gchar  *source,
                    _("Could not open '%s' for writing: %s"),
                    gimp_filename_to_utf8 (dest), g_strerror (errno));
       fclose (sfile);
+      if (old_options_regexp)
+        g_regex_unref (old_options_regexp);
       return FALSE;
     }
 
-  while ((nbytes = fread (buffer, 1, sizeof (buffer), sfile)) > 0)
+  while ((nbytes = fread (buffer + unwritten_len, 1, sizeof (buffer) - unwritten_len, sfile)) > 0 || 
unwritten_len)
     {
-      if (fwrite (buffer, 1, nbytes, dfile) < nbytes)
+      size_t read_len = nbytes + unwritten_len;
+      size_t write_len;
+      gchar* eol;
+      gchar* write_bytes = NULL;
+
+      eol = g_strrstr_len (buffer, read_len, "\n");
+      if (eol)
+        {
+          *eol = '\0';
+          read_len = strlen (buffer) + 1;
+          *eol++ = '\n';
+        }
+      else if (! feof (sfile))
+        {
+          /* We are in unlikely case where a single config line is longer than the buffer! */
+          g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+                       _("Error parsing '%s': line over %ld characters."),
+                       gimp_filename_to_utf8 (dest), sizeof (buffer));
+          fclose (sfile);
+          fclose (dfile);
+          if (old_options_regexp)
+            g_regex_unref (old_options_regexp);
+          return FALSE;
+        }
+
+      if (old_options_regexp && update_callback)
+        {
+          write_bytes = g_regex_replace_eval (old_options_regexp, buffer, read_len, 0, 0, update_callback, 
NULL, error);
+          if (write_bytes == NULL)
+            {
+              /* error already set. */
+              fclose (sfile);
+              fclose (dfile);
+              g_regex_unref (old_options_regexp);
+              return FALSE;
+            }
+          write_len = strlen (write_bytes);
+        }
+      else
+        {
+          write_bytes = buffer;
+          write_len = read_len;
+        }
+
+      if (fwrite (write_bytes, 1, write_len, dfile) < write_len)
         {
           g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                        _("Error writing '%s': %s"),
                        gimp_filename_to_utf8 (dest), g_strerror (errno));
+          if (old_options_regexp && update_callback)
+            {
+              g_free (write_bytes);
+              g_regex_unref (old_options_regexp);
+            }
           fclose (sfile);
           fclose (dfile);
           return FALSE;
         }
+
+      if (old_options_regexp && update_callback)
+        {
+          g_free (write_bytes);
+        }
+
+      if (eol)
+        {
+          unwritten_len = nbytes + unwritten_len - read_len;
+          memmove (buffer, eol, unwritten_len);
+        }
+      else
+        /* EOF */
+        break;
     }
 
   if (ferror (sfile))
@@ -90,6 +165,8 @@ gimp_config_file_copy (const gchar  *source,
                    gimp_filename_to_utf8 (source), g_strerror (errno));
       fclose (sfile);
       fclose (dfile);
+      if (old_options_regexp)
+        g_regex_unref (old_options_regexp);
       return FALSE;
     }
 
@@ -100,6 +177,8 @@ gimp_config_file_copy (const gchar  *source,
       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                    _("Error writing '%s': %s"),
                    gimp_filename_to_utf8 (dest), g_strerror (errno));
+      if (old_options_regexp)
+        g_regex_unref (old_options_regexp);
       return FALSE;
     }
 
@@ -108,6 +187,8 @@ gimp_config_file_copy (const gchar  *source,
       g_chmod (dest, stat_buf.st_mode);
     }
 
+  if (old_options_regexp)
+    g_regex_unref (old_options_regexp);
   return TRUE;
 }
 
@@ -125,7 +206,7 @@ gimp_config_file_backup_on_error (const gchar  *filename,
 
   backup = g_strconcat (filename, "~", NULL);
 
-  success = gimp_config_file_copy (filename, backup, error);
+  success = gimp_config_file_copy (filename, backup, NULL, NULL, error);
 
   if (success)
     g_message (_("There was an error parsing your '%s' file. "
diff --git a/app/config/gimpconfig-file.h b/app/config/gimpconfig-file.h
index 7ece941..2ffc732 100644
--- a/app/config/gimpconfig-file.h
+++ b/app/config/gimpconfig-file.h
@@ -22,8 +22,10 @@
 #define __GIMP_CONFIG_FILE_H__
 
 
-gboolean   gimp_config_file_copy             (const gchar  *source,
-                                              const gchar  *dest,
+gboolean   gimp_config_file_copy             (const gchar        *source,
+                                              const gchar        *dest,
+                                              const gchar        *old_options_regexp,
+                                              GRegexEvalCallback  update_callback,
                                               GError      **error);
 gboolean   gimp_config_file_backup_on_error  (const gchar  *filename,
                                               const gchar  *name,
diff --git a/app/core/gimp-user-install.c b/app/core/gimp-user-install.c
index 3ea5542..9640a02 100644
--- a/app/core/gimp-user-install.c
+++ b/app/core/gimp-user-install.c
@@ -125,9 +125,11 @@ static gboolean  user_install_mkdir              (GimpUserInstall  *install,
                                                   const gchar      *dirname);
 static gboolean  user_install_mkdir_with_parents (GimpUserInstall  *install,
                                                   const gchar      *dirname);
-static gboolean  user_install_file_copy          (GimpUserInstall  *install,
-                                                  const gchar      *source,
-                                                  const gchar      *dest);
+static gboolean  user_install_file_copy          (GimpUserInstall    *install,
+                                                  const gchar        *source,
+                                                  const gchar        *dest,
+                                                  const gchar        *old_options_regexp,
+                                                  GRegexEvalCallback  update_callback);
 static gboolean  user_install_dir_copy           (GimpUserInstall  *install,
                                                   const gchar      *source,
                                                   const gchar      *base);
@@ -366,9 +368,11 @@ user_install_log_error (GimpUserInstall  *install,
 }
 
 static gboolean
-user_install_file_copy (GimpUserInstall *install,
-                        const gchar     *source,
-                        const gchar     *dest)
+user_install_file_copy (GimpUserInstall    *install,
+                        const gchar        *source,
+                        const gchar        *dest,
+                        const gchar        *old_options_regexp,
+                        GRegexEvalCallback  update_callback)
 {
   GError   *error = NULL;
   gboolean  success;
@@ -377,7 +381,7 @@ user_install_file_copy (GimpUserInstall *install,
                     gimp_filename_to_utf8 (dest),
                     gimp_filename_to_utf8 (source));
 
-  success = gimp_config_file_copy (source, dest, &error);
+  success = gimp_config_file_copy (source, dest, old_options_regexp, update_callback, &error);
 
   user_install_log_error (install, &error);
 
@@ -436,6 +440,38 @@ user_install_mkdir_with_parents (GimpUserInstall *install,
   return TRUE;
 }
 
+/* The regexp pattern of all options changed from previous menurc.
+ * Add any pattern that we want to recognize for replacement in the menurc of
+ * the next release*/
+#define MENURC_OVER20_UPDATE_PATTERN "NOMATCH^"
+
+/**
+ * callback to use for updating any change value in the menurc.
+ * data is unused (always NULL).
+ * The updated value will be matched line by line.
+ */
+static gboolean
+user_update_menurc_over20 (const GMatchInfo *matched_value,
+                           GString          *new_value,
+                           gpointer          data)
+{
+  gchar *match;
+  match = g_match_info_fetch (matched_value, 0);
+
+  /* This is an example of how to use it.
+   * If view-close were to be renamed to file-close for instance, we'd add:
+
+  if (strcmp (match, "\"<Actions>/view/view-close\"") == 0)
+    g_string_append (new_value, "\"<Actions>/file/file-close\"");
+  else
+  */
+  /* Should not happen. Just in case we match something unexpected by mistake. */
+  g_string_append (new_value, match);
+
+  g_free (match);
+  return FALSE;
+}
+
 static gboolean
 user_install_dir_copy (GimpUserInstall *install,
                        const gchar     *source,
@@ -477,7 +513,7 @@ user_install_dir_copy (GimpUserInstall *install,
           g_snprintf (dest, sizeof (dest), "%s%c%s",
                       dirname, G_DIR_SEPARATOR, basename);
 
-          if (! user_install_file_copy (install, name, dest))
+          if (! user_install_file_copy (install, name, dest, NULL, NULL))
             {
               g_free (name);
               goto error;
@@ -530,7 +566,7 @@ user_install_create_files (GimpUserInstall *install)
                       gimp_sysconf_directory (), G_DIR_SEPARATOR,
                       gimp_user_install_items[i].name);
 
-          if (! user_install_file_copy (install, source, dest))
+          if (! user_install_file_copy (install, source, dest, NULL, NULL))
             return FALSE;
           break;
         }
@@ -572,6 +608,8 @@ user_install_migrate_files (GimpUserInstall *install)
   while ((basename = g_dir_read_name (dir)) != NULL)
     {
       gchar *source = g_build_filename (install->old_dir, basename, NULL);
+      const gchar* update_pattern = NULL;
+      GRegexEvalCallback update_callback = NULL;
 
       if (g_file_test (source, G_FILE_TEST_IS_REGULAR))
         {
@@ -585,16 +623,19 @@ user_install_migrate_files (GimpUserInstall *install)
               goto next_file;
             }
 
-          /*  skip menurc for gimp 2.0 as the format has changed  */
-          if (install->old_minor == 0 && strcmp (basename, "menurc") == 0)
+          if (strcmp (basename, "menurc") == 0)
             {
-              goto next_file;
+              /*  skip menurc for gimp 2.0 as the format has changed  */
+              if (install->old_minor == 0)
+                goto next_file;
+              update_pattern = MENURC_OVER20_UPDATE_PATTERN;
+              update_callback = user_update_menurc_over20;
             }
 
           g_snprintf (dest, sizeof (dest), "%s%c%s",
                       gimp_directory (), G_DIR_SEPARATOR, basename);
 
-          user_install_file_copy (install, source, dest);
+          user_install_file_copy (install, source, dest, update_pattern, update_callback);
         }
       else if (g_file_test (source, G_FILE_TEST_IS_DIR))
         {
diff --git a/app/menus/menus.c b/app/menus/menus.c
index 3d58dac..842c51c 100644
--- a/app/menus/menus.c
+++ b/app/menus/menus.c
@@ -455,7 +455,7 @@ menus_clear (Gimp    *gimp,
   filename = gimp_personal_rc_file ("menurc");
   source = g_build_filename (gimp_sysconf_directory (), "menurc", NULL);
 
-  if (gimp_config_file_copy (source, filename, NULL))
+  if (gimp_config_file_copy (source, filename, NULL, NULL, NULL))
     {
       menurc_deleted = TRUE;
     }


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