Commit 78b27783 authored by Jehan's avatar Jehan

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.
parent 8a935f05
......@@ -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. "
......
......@@ -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,
......
......@@ -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))
{
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment