summaryrefslogtreecommitdiffstats
path: root/app/core/gimp-user-install.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/core/gimp-user-install.c')
-rw-r--r--app/core/gimp-user-install.c977
1 files changed, 977 insertions, 0 deletions
diff --git a/app/core/gimp-user-install.c b/app/core/gimp-user-install.c
new file mode 100644
index 0000000..2b1c910
--- /dev/null
+++ b/app/core/gimp-user-install.c
@@ -0,0 +1,977 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-user-install.c
+ * Copyright (C) 2000-2008 Michael Natterer and Sven Neumann
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This file contains functions to help migrate the settings from a
+ * previous GIMP version to be used with the current (newer) version.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <libgimpbase/gimpwin32-io.h>
+#endif
+
+#include "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "config/gimpconfig-file.h"
+#include "config/gimprc.h"
+
+#include "gimp-templates.h"
+#include "gimp-tags.h"
+#include "gimp-user-install.h"
+
+#include "gimp-intl.h"
+
+
+struct _GimpUserInstall
+{
+ GObject *gimp;
+
+ gboolean verbose;
+
+ gchar *old_dir;
+ gint old_major;
+ gint old_minor;
+
+ const gchar *migrate;
+
+ GimpUserInstallLogFunc log;
+ gpointer log_data;
+};
+
+typedef enum
+{
+ USER_INSTALL_MKDIR, /* Create the directory */
+ USER_INSTALL_COPY /* Copy from sysconf directory */
+} GimpUserInstallAction;
+
+static const struct
+{
+ const gchar *name;
+ GimpUserInstallAction action;
+}
+gimp_user_install_items[] =
+{
+ { "menurc", USER_INSTALL_COPY },
+ { "brushes", USER_INSTALL_MKDIR },
+ { "dynamics", USER_INSTALL_MKDIR },
+ { "fonts", USER_INSTALL_MKDIR },
+ { "gradients", USER_INSTALL_MKDIR },
+ { "palettes", USER_INSTALL_MKDIR },
+ { "patterns", USER_INSTALL_MKDIR },
+ { "tool-presets", USER_INSTALL_MKDIR },
+ { "plug-ins", USER_INSTALL_MKDIR },
+ { "modules", USER_INSTALL_MKDIR },
+ { "interpreters", USER_INSTALL_MKDIR },
+ { "environ", USER_INSTALL_MKDIR },
+ { "scripts", USER_INSTALL_MKDIR },
+ { "templates", USER_INSTALL_MKDIR },
+ { "themes", USER_INSTALL_MKDIR },
+ { "icons", USER_INSTALL_MKDIR },
+ { "tmp", USER_INSTALL_MKDIR },
+ { "curves", USER_INSTALL_MKDIR },
+ { "levels", USER_INSTALL_MKDIR },
+ { "filters", USER_INSTALL_MKDIR },
+ { "fractalexplorer", USER_INSTALL_MKDIR },
+ { "gfig", USER_INSTALL_MKDIR },
+ { "gflare", USER_INSTALL_MKDIR },
+ { "gimpressionist", USER_INSTALL_MKDIR }
+};
+
+
+static gboolean user_install_detect_old (GimpUserInstall *install,
+ const gchar *gimp_dir);
+static gchar * user_install_old_style_gimpdir (void);
+
+static void user_install_log (GimpUserInstall *install,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+static void user_install_log_newline (GimpUserInstall *install);
+static void user_install_log_error (GimpUserInstall *install,
+ GError **error);
+
+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,
+ const gchar *old_options_regexp,
+ GRegexEvalCallback update_callback);
+static gboolean user_install_dir_copy (GimpUserInstall *install,
+ gint level,
+ const gchar *source,
+ const gchar *base,
+ const gchar *update_pattern,
+ GRegexEvalCallback update_callback);
+
+static gboolean user_install_create_files (GimpUserInstall *install);
+static gboolean user_install_migrate_files (GimpUserInstall *install);
+
+
+/* public functions */
+
+GimpUserInstall *
+gimp_user_install_new (GObject *gimp,
+ gboolean verbose)
+{
+ GimpUserInstall *install = g_slice_new0 (GimpUserInstall);
+
+ install->gimp = gimp;
+ install->verbose = verbose;
+
+ user_install_detect_old (install, gimp_directory ());
+
+#ifdef PLATFORM_OSX
+ /* The config path on OSX has for a very short time frame (2.8.2 only)
+ been "~/Library/GIMP". It changed to "~/Library/Application Support"
+ in 2.8.4 and was in the home folder (as was other UNIX) before. */
+
+ if (! install->old_dir)
+ {
+ gchar *dir;
+ NSAutoreleasePool *pool;
+ NSArray *path;
+ NSString *library_dir;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
+ NSUserDomainMask, YES);
+ library_dir = [path objectAtIndex:0];
+
+ dir = g_build_filename ([library_dir UTF8String],
+ GIMPDIR, GIMP_USER_VERSION, NULL);
+
+ [pool drain];
+
+ user_install_detect_old (install, dir);
+ g_free (dir);
+ }
+
+#endif
+
+ if (! install->old_dir)
+ {
+ /* if the default XDG-style config directory was not found, try
+ * the "old-style" path in the home folder.
+ */
+ gchar *dir = user_install_old_style_gimpdir ();
+ user_install_detect_old (install, dir);
+ g_free (dir);
+ }
+
+ return install;
+}
+
+gboolean
+gimp_user_install_run (GimpUserInstall *install)
+{
+ gchar *dirname;
+
+ g_return_val_if_fail (install != NULL, FALSE);
+
+ dirname = g_filename_display_name (gimp_directory ());
+
+ if (install->migrate)
+ user_install_log (install,
+ _("It seems you have used GIMP %s before. "
+ "GIMP will now migrate your user settings to '%s'."),
+ install->migrate, dirname);
+ else
+ user_install_log (install,
+ _("It appears that you are using GIMP for the "
+ "first time. GIMP will now create a folder "
+ "named '%s' and copy some files to it."),
+ dirname);
+
+ g_free (dirname);
+
+ user_install_log_newline (install);
+
+ if (! user_install_mkdir_with_parents (install, gimp_directory ()))
+ return FALSE;
+
+ if (install->migrate)
+ if (! user_install_migrate_files (install))
+ return FALSE;
+
+ return user_install_create_files (install);
+}
+
+void
+gimp_user_install_free (GimpUserInstall *install)
+{
+ g_return_if_fail (install != NULL);
+
+ g_free (install->old_dir);
+
+ g_slice_free (GimpUserInstall, install);
+}
+
+void
+gimp_user_install_set_log_handler (GimpUserInstall *install,
+ GimpUserInstallLogFunc log,
+ gpointer user_data)
+{
+ g_return_if_fail (install != NULL);
+
+ install->log = log;
+ install->log_data = user_data;
+}
+
+
+/* Local functions */
+
+static gboolean
+user_install_detect_old (GimpUserInstall *install,
+ const gchar *gimp_dir)
+{
+ gchar *dir = g_strdup (gimp_dir);
+ gchar *version;
+ gboolean migrate = FALSE;
+
+ version = strstr (dir, GIMP_APP_VERSION);
+
+ if (version)
+ {
+ gint i;
+
+ for (i = (GIMP_MINOR_VERSION & ~1); i >= 0; i -= 2)
+ {
+ /* we assume that GIMP_APP_VERSION is in the form '2.x' */
+ g_snprintf (version + 2, 2, "%d", i);
+
+ migrate = g_file_test (dir, G_FILE_TEST_IS_DIR);
+
+ if (migrate)
+ {
+#ifdef GIMP_UNSTABLE
+ g_printerr ("gimp-user-install: migrating from %s\n", dir);
+#endif
+ install->old_major = 2;
+ install->old_minor = i;
+
+ break;
+ }
+ }
+ }
+
+ if (migrate)
+ {
+ install->old_dir = dir;
+ install->migrate = (const gchar *) version;
+ }
+ else
+ {
+ g_free (dir);
+ }
+
+ return migrate;
+}
+
+static gchar *
+user_install_old_style_gimpdir (void)
+{
+ const gchar *home_dir = g_get_home_dir ();
+ gchar *gimp_dir = NULL;
+
+ if (home_dir)
+ {
+ gimp_dir = g_build_filename (home_dir, ".gimp-" GIMP_APP_VERSION, NULL);
+ }
+ else
+ {
+ gchar *user_name = g_strdup (g_get_user_name ());
+ gchar *subdir_name;
+
+#ifdef G_OS_WIN32
+ gchar *p = user_name;
+
+ while (*p)
+ {
+ /* Replace funny characters in the user name with an
+ * underscore. The code below also replaces some
+ * characters that in fact are legal in file names, but
+ * who cares, as long as the definitely illegal ones are
+ * caught.
+ */
+ if (!g_ascii_isalnum (*p) && !strchr ("-.,@=", *p))
+ *p = '_';
+ p++;
+ }
+#endif
+
+#ifndef G_OS_WIN32
+ g_message ("warning: no home directory.");
+#endif
+ subdir_name = g_strconcat (".gimp-" GIMP_APP_VERSION ".", user_name, NULL);
+ gimp_dir = g_build_filename (gimp_data_directory (),
+ subdir_name,
+ NULL);
+ g_free (user_name);
+ g_free (subdir_name);
+ }
+
+ return gimp_dir;
+}
+
+static void
+user_install_log (GimpUserInstall *install,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+
+ va_start (args, format);
+
+ if (format)
+ {
+ gchar *message = g_strdup_vprintf (format, args);
+
+ if (install->verbose)
+ g_print ("%s\n", message);
+
+ if (install->log)
+ install->log (message, FALSE, install->log_data);
+
+ g_free (message);
+ }
+
+ va_end (args);
+}
+
+static void
+user_install_log_newline (GimpUserInstall *install)
+{
+ if (install->verbose)
+ g_print ("\n");
+
+ if (install->log)
+ install->log (NULL, FALSE, install->log_data);
+}
+
+static void
+user_install_log_error (GimpUserInstall *install,
+ GError **error)
+{
+ if (error && *error)
+ {
+ const gchar *message = ((*error)->message ?
+ (*error)->message : "(unknown error)");
+
+ if (install->log)
+ install->log (message, TRUE, install->log_data);
+ else
+ g_print ("error: %s\n", message);
+
+ g_clear_error (error);
+ }
+}
+
+static gboolean
+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;
+
+ user_install_log (install, _("Copying file '%s' from '%s'..."),
+ gimp_filename_to_utf8 (dest),
+ gimp_filename_to_utf8 (source));
+
+ success = gimp_config_file_copy (source, dest, old_options_regexp, update_callback, &error);
+
+ user_install_log_error (install, &error);
+
+ return success;
+}
+
+static gboolean
+user_install_mkdir (GimpUserInstall *install,
+ const gchar *dirname)
+{
+ user_install_log (install, _("Creating folder '%s'..."),
+ gimp_filename_to_utf8 (dirname));
+
+ if (g_mkdir (dirname,
+ S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH) == -1)
+ {
+ GError *error = NULL;
+
+ g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Cannot create folder '%s': %s"),
+ gimp_filename_to_utf8 (dirname), g_strerror (errno));
+
+ user_install_log_error (install, &error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+user_install_mkdir_with_parents (GimpUserInstall *install,
+ const gchar *dirname)
+{
+ user_install_log (install, _("Creating folder '%s'..."),
+ gimp_filename_to_utf8 (dirname));
+
+ if (g_mkdir_with_parents (dirname,
+ S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH) == -1)
+ {
+ GError *error = NULL;
+
+ g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Cannot create folder '%s': %s"),
+ gimp_filename_to_utf8 (dirname), g_strerror (errno));
+
+ user_install_log_error (install, &error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* The regexp pattern of all options changed from menurc of GIMP 2.8.
+ * Add any pattern that we want to recognize for replacement in the menurc of
+ * the next release
+ */
+#define MENURC_OVER20_UPDATE_PATTERN \
+ "\"<Actions>/buffers/buffers-paste-as-new\"" "|" \
+ "\"<Actions>/edit/edit-paste-as-new\"" "|" \
+ "\"<Actions>/file/file-export\"" "|" \
+ "\"<Actions>/file/file-export-to\"" "|" \
+ "\"<Actions>/layers/layers-text-tool\"" "|" \
+ "\"<Actions>/plug-in/plug-in-gauss\"" "|" \
+ "\"<Actions>/tools/tools-value-[1-4]-.*\"" "|" \
+ "\"<Actions>/vectors/vectors-path-tool\"" "|" \
+ "\"<Actions>/tools/tools-blend\""
+
+/**
+ * callback to use for updating a menurc from GIMP over 2.0.
+ * 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 = g_match_info_fetch (matched_value, 0);
+
+ /* "*-paste-as-new" renamed to "*-paste-as-new-image"
+ */
+ if (g_strcmp0 (match, "\"<Actions>/buffers/buffers-paste-as-new\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/buffers/buffers-paste-as-new-image\"");
+ }
+ else if (g_strcmp0 (match, "\"<Actions>/edit/edit-paste-as-new\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/edit/edit-paste-as-new-image\"");
+ }
+ /* file-export-* changes to follow file-save-* patterns. Actions
+ * available since GIMP 2.8, changed for 2.10 in commit 4b14ed2.
+ */
+ else if (g_strcmp0 (match, "\"<Actions>/file/file-export\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/file/file-export-as\"");
+ }
+ else if (g_strcmp0 (match, "\"<Actions>/file/file-export-to\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/file/file-export\"");
+ }
+ else if (g_strcmp0 (match, "\"<Actions>/layers/layers-text-tool\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/layers/layers-edit\"");
+ }
+ /* plug-in-gauss doesn't exist anymore since commit ff59aebbe88.
+ * The expected replacement would be filters-gaussian-blur which is
+ * gegl:gaussian-blur operation. See also bug 775931.
+ */
+ else if (g_strcmp0 (match, "\"<Actions>/plug-in/plug-in-gauss\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/filters/filters-gaussian-blur\"");
+ }
+ /* Tools settings renamed more user-friendly. Actions available
+ * since GIMP 2.4, changed for 2.10 in commit 0bdb747.
+ */
+ else if (g_str_has_prefix (match, "\"<Actions>/tools/tools-value-1-"))
+ {
+ g_string_append (new_value, "\"<Actions>/tools/tools-opacity-");
+ g_string_append (new_value, match + 31);
+ }
+ else if (g_str_has_prefix (match, "\"<Actions>/tools/tools-value-2-"))
+ {
+ g_string_append (new_value, "\"<Actions>/tools/tools-size-");
+ g_string_append (new_value, match + 31);
+ }
+ else if (g_str_has_prefix (match, "\"<Actions>/tools/tools-value-3-"))
+ {
+ g_string_append (new_value, "\"<Actions>/tools/tools-aspect-");
+ g_string_append (new_value, match + 31);
+ }
+ else if (g_str_has_prefix (match, "\"<Actions>/tools/tools-value-4-"))
+ {
+ g_string_append (new_value, "\"<Actions>/tools/tools-angle-");
+ g_string_append (new_value, match + 31);
+ }
+ else if (g_strcmp0 (match, "\"<Actions>/vectors/vectors-path-tool\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/vectors/vectors-edit\"");
+ }
+ else if (g_strcmp0 (match, "\"<Actions>/tools/tools-blend\"") == 0)
+ {
+ g_string_append (new_value, "\"<Actions>/tools/tools-gradient\"");
+ }
+ /* Should not happen. Just in case we match something unexpected by
+ * mistake.
+ */
+ else
+ {
+ g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match);
+ g_string_append (new_value, match);
+ }
+
+ g_free (match);
+ return FALSE;
+}
+
+#define CONTROLLERRC_UPDATE_PATTERN \
+ "\\(map \"(scroll|cursor)-[^\"]*\\bcontrol\\b[^\"]*\""
+
+static gboolean
+user_update_controllerrc (const GMatchInfo *matched_value,
+ GString *new_value,
+ gpointer data)
+{
+ gchar *original;
+ gchar *replacement;
+ GRegex *regexp = NULL;
+
+ /* No need of a complicated pattern here.
+ * CONTROLLERRC_UPDATE_PATTERN took care of it first.
+ */
+ regexp = g_regex_new ("\\bcontrol\\b", 0, 0, NULL);
+ original = g_match_info_fetch (matched_value, 0);
+
+ replacement = g_regex_replace (regexp, original, -1, 0,
+ "primary", 0, NULL);
+ g_string_append (new_value, replacement);
+
+ g_free (original);
+ g_free (replacement);
+ g_regex_unref (regexp);
+
+ return FALSE;
+}
+
+#define GIMPRC_UPDATE_PATTERN \
+ "\\(theme [^)]*\\)" "|" \
+ "\\(.*-path [^)]*\\)"
+
+static gboolean
+user_update_gimprc (const GMatchInfo *matched_value,
+ GString *new_value,
+ gpointer data)
+{
+ /* Do not migrate paths and themes from GIMP < 2.10. */
+ return FALSE;
+}
+
+#define GIMPRESSIONIST_UPDATE_PATTERN \
+ "selectedbrush=Brushes/paintbrush.pgm"
+
+static gboolean
+user_update_gimpressionist (const GMatchInfo *matched_value,
+ GString *new_value,
+ gpointer data)
+{
+ gchar *match = g_match_info_fetch (matched_value, 0);
+
+ /* See bug 791934: both brushes are identical. */
+ if (g_strcmp0 (match, "selectedbrush=Brushes/paintbrush.pgm") == 0)
+ {
+ g_string_append (new_value, "selectedbrush=Brushes/paintbrush01.pgm");
+ }
+ else
+ {
+ g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match);
+ g_string_append (new_value, match);
+ }
+
+ g_free (match);
+ return FALSE;
+}
+
+#define TOOL_PRESETS_UPDATE_PATTERN \
+ "GimpImageMapOptions" "|" \
+ "GimpBlendOptions" "|" \
+ "gimp-blend-tool" "|" \
+ "gimp-tool-blend"
+
+static gboolean
+user_update_tool_presets (const GMatchInfo *matched_value,
+ GString *new_value,
+ gpointer data)
+{
+ gchar *match = g_match_info_fetch (matched_value, 0);
+
+ if (g_strcmp0 (match, "GimpImageMapOptions") == 0)
+ {
+ g_string_append (new_value, "GimpFilterOptions");
+ }
+ else if (g_strcmp0 (match, "GimpBlendOptions") == 0)
+ {
+ g_string_append (new_value, "GimpGradientOptions");
+ }
+ else if (g_strcmp0 (match, "gimp-blend-tool") == 0)
+ {
+ g_string_append (new_value, "gimp-gradient-tool");
+ }
+ else if (g_strcmp0 (match, "gimp-tool-blend") == 0)
+ {
+ g_string_append (new_value, "gimp-tool-gradient");
+ }
+ else
+ {
+ g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match);
+ g_string_append (new_value, match);
+ }
+
+ g_free (match);
+ return FALSE;
+}
+
+/* Actually not only for contextrc, but all other files where
+ * gimp-blend-tool may appear. Apparently that is also "devicerc", as
+ * well as "toolrc" (but this one is skipped anyway).
+ */
+#define CONTEXTRC_UPDATE_PATTERN "gimp-blend-tool"
+
+static gboolean
+user_update_contextrc_over20 (const GMatchInfo *matched_value,
+ GString *new_value,
+ gpointer data)
+{
+ gchar *match = g_match_info_fetch (matched_value, 0);
+
+ if (g_strcmp0 (match, "gimp-blend-tool") == 0)
+ {
+ g_string_append (new_value, "gimp-gradient-tool");
+ }
+ else
+ {
+ g_message ("(WARNING) %s: invalid match \"%s\"", G_STRFUNC, match);
+ g_string_append (new_value, match);
+ }
+
+ g_free (match);
+ return FALSE;
+}
+
+static gboolean
+user_install_dir_copy (GimpUserInstall *install,
+ gint level,
+ const gchar *source,
+ const gchar *base,
+ const gchar *update_pattern,
+ GRegexEvalCallback update_callback)
+{
+ GDir *source_dir = NULL;
+ GDir *dest_dir = NULL;
+ gchar dest[1024];
+ const gchar *basename;
+ gchar *dirname = NULL;
+ gchar *name;
+ GError *error = NULL;
+ gboolean success = FALSE;
+
+ if (level >= 5)
+ {
+ /* Config migration is recursive, but we can't go on forever,
+ * since we may fall into recursive symlinks in particular (which
+ * is a security risk to fill a disk, and would also block GIMP
+ * forever at migration stage).
+ * Let's just break the recursivity at 5 levels, which is just an
+ * arbitrary value (but I don't think there should be any data
+ * deeper than this).
+ */
+ goto error;
+ }
+
+ name = g_path_get_basename (source);
+ dirname = g_build_filename (base, name, NULL);
+ g_free (name);
+
+ success = user_install_mkdir (install, dirname);
+ if (! success)
+ goto error;
+
+ success = (dest_dir = g_dir_open (dirname, 0, &error)) != NULL;
+ if (! success)
+ goto error;
+
+ success = (source_dir = g_dir_open (source, 0, &error)) != NULL;
+ if (! success)
+ goto error;
+
+ while ((basename = g_dir_read_name (source_dir)) != NULL)
+ {
+ name = g_build_filename (source, basename, NULL);
+
+ if (g_file_test (name, G_FILE_TEST_IS_REGULAR))
+ {
+ g_snprintf (dest, sizeof (dest), "%s%c%s",
+ dirname, G_DIR_SEPARATOR, basename);
+
+ success = user_install_file_copy (install, name, dest,
+ update_pattern,
+ update_callback);
+ if (! success)
+ {
+ g_free (name);
+ goto error;
+ }
+ }
+ else
+ {
+ user_install_dir_copy (install, level + 1, name, dirname,
+ update_pattern, update_callback);
+ }
+
+ g_free (name);
+ }
+
+ error:
+ user_install_log_error (install, &error);
+
+ if (source_dir)
+ g_dir_close (source_dir);
+
+ if (dest_dir)
+ g_dir_close (dest_dir);
+
+ if (dirname)
+ g_free (dirname);
+
+ return success;
+}
+
+static gboolean
+user_install_create_files (GimpUserInstall *install)
+{
+ gchar dest[1024];
+ gchar source[1024];
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (gimp_user_install_items); i++)
+ {
+ g_snprintf (dest, sizeof (dest), "%s%c%s",
+ gimp_directory (),
+ G_DIR_SEPARATOR,
+ gimp_user_install_items[i].name);
+
+ if (g_file_test (dest, G_FILE_TEST_EXISTS))
+ continue;
+
+ switch (gimp_user_install_items[i].action)
+ {
+ case USER_INSTALL_MKDIR:
+ if (! user_install_mkdir (install, dest))
+ return FALSE;
+ break;
+
+ case USER_INSTALL_COPY:
+ g_snprintf (source, sizeof (source), "%s%c%s",
+ gimp_sysconf_directory (), G_DIR_SEPARATOR,
+ gimp_user_install_items[i].name);
+
+ if (! user_install_file_copy (install, source, dest, NULL, NULL))
+ return FALSE;
+ break;
+ }
+ }
+
+ g_snprintf (dest, sizeof (dest), "%s%c%s",
+ gimp_directory (), G_DIR_SEPARATOR, "tags.xml");
+
+ if (! g_file_test (dest, G_FILE_TEST_IS_REGULAR))
+ {
+ /* if there was no tags.xml, install it with default tag set.
+ */
+ if (! gimp_tags_user_install ())
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+user_install_migrate_files (GimpUserInstall *install)
+{
+ GDir *dir;
+ const gchar *basename;
+ gchar dest[1024];
+ GimpRc *gimprc;
+ GError *error = NULL;
+
+ dir = g_dir_open (install->old_dir, 0, &error);
+
+ if (! dir)
+ {
+ user_install_log_error (install, &error);
+ return FALSE;
+ }
+
+ while ((basename = g_dir_read_name (dir)) != NULL)
+ {
+ gchar *source = g_build_filename (install->old_dir, basename, NULL);
+
+ if (g_file_test (source, G_FILE_TEST_IS_REGULAR))
+ {
+ const gchar *update_pattern = NULL;
+ GRegexEvalCallback update_callback = NULL;
+
+ /* skip these files for all old versions */
+ if (strcmp (basename, "documents") == 0 ||
+ g_str_has_prefix (basename, "gimpswap.") ||
+ strcmp (basename, "pluginrc") == 0 ||
+ strcmp (basename, "themerc") == 0 ||
+ strcmp (basename, "toolrc") == 0)
+ {
+ goto next_file;
+ }
+ else if (strcmp (basename, "menurc") == 0)
+ {
+ switch (install->old_minor)
+ {
+ case 0:
+ /* skip menurc for gimp 2.0 as the format has changed */
+ goto next_file;
+ break;
+ default:
+ update_pattern = MENURC_OVER20_UPDATE_PATTERN;
+ update_callback = user_update_menurc_over20;
+ break;
+ }
+ }
+ else if (strcmp (basename, "controllerrc") == 0)
+ {
+ update_pattern = CONTROLLERRC_UPDATE_PATTERN;
+ update_callback = user_update_controllerrc;
+ }
+ else if (strcmp (basename, "gimprc") == 0)
+ {
+ update_pattern = GIMPRC_UPDATE_PATTERN;
+ update_callback = user_update_gimprc;
+ }
+ else if (strcmp (basename, "contextrc") == 0 ||
+ strcmp (basename, "devicerc") == 0)
+ {
+ update_pattern = CONTEXTRC_UPDATE_PATTERN;
+ update_callback = user_update_contextrc_over20;
+ }
+
+ g_snprintf (dest, sizeof (dest), "%s%c%s",
+ gimp_directory (), G_DIR_SEPARATOR, basename);
+
+ user_install_file_copy (install, source, dest,
+ update_pattern, update_callback);
+ }
+ else if (g_file_test (source, G_FILE_TEST_IS_DIR))
+ {
+ const gchar *update_pattern = NULL;
+ GRegexEvalCallback update_callback = NULL;
+
+ /* skip these directories for all old versions */
+ if (strcmp (basename, "tmp") == 0 ||
+ strcmp (basename, "tool-options") == 0 ||
+ strcmp (basename, "themes") == 0)
+ {
+ goto next_file;
+ }
+
+ if (strcmp (basename, "gimpressionist") == 0)
+ {
+ update_pattern = GIMPRESSIONIST_UPDATE_PATTERN;
+ update_callback = user_update_gimpressionist;
+ }
+ else if (strcmp (basename, "tool-presets") == 0)
+ {
+ update_pattern = TOOL_PRESETS_UPDATE_PATTERN;
+ update_callback = user_update_tool_presets;
+ }
+ user_install_dir_copy (install, 0, source, gimp_directory (),
+ update_pattern, update_callback);
+ }
+
+ next_file:
+ g_free (source);
+ }
+
+ /* create the tmp directory that was explicitly not copied */
+
+ g_snprintf (dest, sizeof (dest), "%s%c%s",
+ gimp_directory (), G_DIR_SEPARATOR, "tmp");
+
+ user_install_mkdir (install, dest);
+ g_dir_close (dir);
+
+ gimp_templates_migrate (install->old_dir);
+
+ gimprc = gimp_rc_new (install->gimp, NULL, NULL, FALSE);
+ gimp_rc_migrate (gimprc);
+ gimp_rc_save (gimprc);
+ g_object_unref (gimprc);
+
+ return TRUE;
+}