/* $Id$ Copyright (C) 2005-2010 tooar This file is part of emelFM2. emelFM2 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, or (at your option) any later version. emelFM2 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 emelFM2; see the file GPL. If not, see http://www.gnu.org/licenses. */ /** @file plugins/e2p_upgrade.c @brief plugin for updating config files when a new emelFM2 version so requires This file contains functions that help upgrading the default configuration data file to the current version. Note that upgrading may also be needed for imported config data - refer to the config plugin */ #include "emelfm2.h" #include #include "e2_plugins.h" #include "e2_option.h" #include "e2_output.h" #include "e2_dialog.h" #include "e2_task.h" #include "e2_complete.h" static gboolean cancelled = FALSE; static gchar *default_msg = N_("Configuration arrangements for this version %s of %s are considerably " "different from those of old versions. To reliably ensure access to the " "program's current features, it is best to start with fresh settings.\n" "If you proceed, the superseded configuration files in\n %s will have '.save' " "appended to their names.\nFeel free to delete them." ); #if 0 static gchar *option_msg = N _ ("Several default configuration settings of this version %s of %s" " are different from those of recent versions (see changelog).\n" "If you click OK, those settings will be updated where possible.\n" "Or else you can Cancel, and later, via the configuration dialog, manually" "change individual settings, or change all settings to current defaults." ); #endif //0 static void _e2p_upgrade_reconfig (void) { //prevent attempts to clean non-existent backup data guint i; gpointer *walker; E2_OptionSet *set; for (i = 0, walker = options_array->pdata; i < options_array->len; i++, walker++) { set = *walker; if (set->type == E2_OPTION_TYPE_TREE) set->ex.tree.def.tree_strings = NULL; //CHECKME leak ? } //clear current option values e2_option_clear_data (); e2_option_default_register (); e2_option_file_read (); } //CHECKME assumes BGL is closed, native file only static void _e2p_upgrade_backup (gchar *file) { gchar *cfg_file = g_strdup_printf ("%s"G_DIR_SEPARATOR_S"%s", e2_cl_options.config_dir, file); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); #ifdef E2_VFS VPATH ddata = { local, NULL }; if (e2_fs_access (&ddata, F_OK E2_ERR_NONE()) == 0) //traverse link, if any #else if (e2_fs_access (local, F_OK E2_ERR_NONE()) == 0) //traverse link, if any #endif { gchar *saved_file = g_strconcat (local, ".save", NULL); gdk_threads_leave (); //downstream errors invoke local mutex locking #ifdef E2_VFS VPATH sdata = { saved_file, NULL }; e2_task_backend_rename (&ddata, &sdata); #else e2_task_backend_rename (local, saved_file); #endif gdk_threads_enter (); g_free (saved_file); } g_free (cfg_file); F_FREE (local, cfg_file); } static gboolean _e2p_upgrade_run (const gchar *command, const gchar *cfg_path) { if (system (command) == 0) cancelled = FALSE; else { cancelled = TRUE; printd (DEBUG, "failed command '%s'", command); gchar *revert = g_strconcat ("mv -f ", cfg_path, ".save ", cfg_path, NULL); gint success = system (revert); if (success != 0) printd (DEBUG, "failed reversion command '%s'", revert); g_free (revert); } return !cancelled; } static gint _e2p_upgrade_dialog (gchar *msg) { //main button structs not created yet E2_Button yes_btn = { _("_Apply"), #ifdef E2_IMAGECACHE //FIXME gtk images not yet available NULL, #else GTK_STOCK_YES, #endif NULL, E2_BTN_DEFAULT, E2_BTN_DEFAULT, GTK_RESPONSE_YES }; E2_Button no_btn = { _("_Cancel"), #ifdef E2_IMAGECACHE //FIXME gtk images not yet available NULL, #else GTK_STOCK_NO, #endif NULL, E2_BTN_DEFAULT, E2_BTN_DEFAULT, GTK_RESPONSE_NO }; GtkWidget *dialog = e2_dialog_create ( #ifdef E2_IMAGECACHE //FIXME gtk images not yet available NULL, #else GTK_STOCK_DIALOG_INFO, #endif msg, _("update information"), DEFAULT_RESPONSE_CB, NULL); e2_dialog_show (dialog, NULL, 0, &yes_btn, &no_btn, NULL); // gint choice = e2_dialog_run_simple (dialog, NULL); //not a local loop, as main-window (for fake event) & options not yet available gint choice = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); cancelled = (choice != GTK_RESPONSE_YES); return choice; } //#if 0 static gchar *_e2p_upgrade_get_sed (void) { gchar *sed = g_find_program_in_path ("sed"); if (sed == NULL) { //FIXME should always warn the user about this printd (ERROR, "can't find 'sed' so i can't upgrade the config file"); cancelled = TRUE; } return sed; } //#endif #ifdef E2_VERSIONDOCS static void _e2p_upgrade_numbers (void) { gchar *sed = _e2p_upgrade_get_sed (); if (sed != NULL) { gchar *cfg_file = g_build_filename (e2_cl_options.config_dir, default_config_file, NULL); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); gchar *localtmp = e2_utils_get_tempname (local); gchar *command = g_strconcat ("cp -f ", local, " ", localtmp, ".save;", sed, " -e '1s/", app.cfgfile_version, "/"VERSION RELEASE"/'", " -e '2,$s/0\\.[0-9]\\.[0-9]/"VERSION"/'", " ",localtmp,".save >",local,NULL); if (!_e2p_upgrade_run (command, localtmp)) printd (WARN, "failed to execute command to upgrade config version no's"); g_free (sed); g_free (cfg_file); F_FREE (local, cfg_file); g_free (localtmp); g_free (command); } else cancelled = TRUE; } #endif #ifdef E2_POLKIT /** @brief upgrade su-related strings @return TRUE if the upgrade was done */ static gboolean _e2p_upgrade_su (void) { gchar *sed = _e2p_upgrade_get_sed (); if (sed != NULL) { const gchar *prompt1 = _("Enter command:"); const gchar *prompt2 = _("Done. Press enter "); //commandbar item gchar *oldstr1 = g_strconcat("\\|xterm\\|-e 'su -c \\\"%\\{\\(root-commands\\)@",prompt1,"\\}\\\";echo -n \\\"",prompt2,"\\\";read'",NULL); gchar *newstr1 = g_strconcat("|pkexec|%{(root-commands)@",prompt1,"}",NULL); //alias //2nd (.*) around "\2" so this can work (and be inserted in the replacement string) gchar *oldstr2 = g_strconcat("\\|xterm -e sh -c 'su -c \\\"(.*)\\\";echo -n \\\"",prompt2,"\\\";read'",NULL); const gchar *newstr2 = "|pkexec \\2"; gchar *cfg_file = g_build_filename (e2_cl_options.config_dir, default_config_file, NULL); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;", sed, " -r" //support back-referencing " -e \"s~(.*)",oldstr1,"~\\1",newstr1,"~\"", " -e \"s~(.*)",oldstr2,"~\\1",newstr2,"~\"", " ",local,".save >",local,NULL); gboolean success = _e2p_upgrade_run (command, local); g_free (oldstr1); g_free (newstr1); g_free (oldstr2); g_free (cfg_file); F_FREE (local, cfg_file); g_free (command); g_free (sed); if (success) return TRUE; printd (WARN, "failed to execute command to do su-upgrade"); } cancelled = TRUE; return FALSE; } #endif static void _e2p_upgrade_pre_0_1 (void) { gchar *msg = g_strdup_printf (gettext (default_msg), VERSION, PROGNAME, e2_cl_options.config_dir); gint choice = _e2p_upgrade_dialog (msg); g_free (msg); if (choice == GTK_RESPONSE_YES) { _e2p_upgrade_backup ("config"); // _e2p_upgrade_backup ("cache"); this one is ok to leave as is _e2p_upgrade_backup ("filetypes"); _e2p_upgrade_backup ("plugins"); _e2p_upgrade_backup ("settings"); e2_option_clear_data (); e2_option_default_register (); } else exit (1); } static void _e2p_upgrade_0_4_1 (void) { //get rid of outdated cache flags for find plugin, which hasn't been loaded yet e2_cache_clean1 ("find-plugin-flags"); } static void _e2p_upgrade_0_4_5 (void) { //one dialog only ! gchar *msg = g_strdup_printf (gettext (default_msg), VERSION, PROGNAME, e2_cl_options.config_dir); gint choice = _e2p_upgrade_dialog (msg); g_free (msg); if (choice == GTK_RESPONSE_YES) _e2p_upgrade_backup (default_config_file); else cancelled = TRUE; } static void _e2p_upgrade_0_5_1 (void) { gchar *sed = _e2p_upgrade_get_sed (); if (sed != NULL) { //keybinding to add gchar *oldstr1 = g_strconcat("\t\t\t|i|false|",_A(7),".",_A(57),"|",NULL); gchar *newstr1 = g_strconcat("\t\t\t|d|false|",_A(7),".",_A(80),"|",NULL); gchar *cfg_file = g_build_filename (e2_cl_options.config_dir, default_config_file, NULL); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;", sed, #ifndef E2_VERSIONDOCS " -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'", #endif " -e '/",oldstr1,"$/a\\\n",newstr1,"'", " ",local,".save >",local,NULL); if (!_e2p_upgrade_run (command, local)) printd (WARN, "failed to execute command to do upgrade 0.5.1"); g_free (oldstr1); g_free (newstr1); g_free (cfg_file); F_FREE (local, cfg_file); g_free (command); g_free (sed); } else _e2p_upgrade_0_4_5 (); //suggest default } static void _e2p_upgrade_0_5_1_1 (void) { gchar *sed = _e2p_upgrade_get_sed (); if (sed != NULL) { //keybindings to change gchar *oldstr1 = g_strconcat(_A(10),".",_A(31),"|*,1",NULL); gchar *newstr1 = g_strconcat("!",_A(10),".",_A(31),"|1,*",NULL); gchar *oldstr2 = g_strconcat(_A(10),".",_A(31),"|*,0",NULL); gchar *newstr2 = g_strconcat("!",_A(10),".",_A(31),"|0,*",NULL); gchar *oldstr3 = g_strconcat(_A(14),".",_A(31),"|*,1",NULL); gchar *newstr3 = g_strconcat("!",_A(14),".",_A(31),"|0,*",NULL); gchar *oldstr4 = g_strconcat(_A(14),".",_A(31),"|*,0",NULL); gchar *newstr4 = g_strconcat("!",_A(14),".",_A(31),"|1,*",NULL); gchar *cfg_file = g_build_filename (e2_cl_options.config_dir, default_config_file, NULL); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;", sed, #ifndef E2_VERSIONDOCS " -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'", #endif " -e 's/",oldstr1,"/",newstr1,"/'", " -e 's/",oldstr2,"/",newstr2,"/'", " -e 's/",oldstr3,"/",newstr3,"/'", " -e 's/",oldstr4,"/",newstr4,"/'", " ",local,".save >",local,NULL); if (!_e2p_upgrade_run (command, local)) printd (WARN, "failed to execute command to do upgrade 0.5.1.1"); g_free (oldstr1); g_free (newstr1); g_free (oldstr2); g_free (newstr2); g_free (oldstr3); g_free (newstr3); g_free (oldstr4); g_free (newstr4); g_free (cfg_file); F_FREE (local, cfg_file); g_free (command); g_free (sed); } else _e2p_upgrade_0_4_5 (); //suggest default } static void _e2p_upgrade_0_6_0 (void) { //update key-shortcut translations E2_OptionSet *set = e2_option_get ("keybindings"); if (set->ex.tree.synced) //this set's data processed already { GtkTreeIter iter; if (gtk_tree_model_get_iter_first (set->ex.tree.model, &iter)) e2_keybinding_localise (set->ex.tree.model, &iter); } //else FIXME } static void _e2p_upgrade_0_7_2 (void) { gchar *sed = _e2p_upgrade_get_sed (); if (sed != NULL) { //context menu item add gchar *oldstr1 = g_strconcat("\t",_("_Edit bookmarks.."),"|gtk-preferences|false|false|",_A(3),".",_C(1),"|",NULL); gchar *newstr1 = g_strconcat(_("_History"),"|gtk-jump-to|false|false|",_A(8),".",_A(27),"|",NULL); gchar *cfg_file = g_build_filename (e2_cl_options.config_dir, default_config_file, NULL); gchar *local = F_FILENAME_TO_LOCALE (cfg_file); gchar *command = g_strconcat ("cp -f ", local, " ", local, ".save;", sed, #ifndef E2_VERSIONDOCS " -e '1s/", app.cfgfile_version, "/", VERSION RELEASE, "/'", #endif " -e '/",oldstr1,"$/a\\\n",newstr1,"'", " ",local,".save >",local,NULL); if (!_e2p_upgrade_run (command, local)) printd (WARN, "failed to execute command to do upgrade 0.7.2"); g_free (oldstr1); g_free (newstr1); g_free (cfg_file); F_FREE (local, cfg_file); g_free (command); g_free (sed); } else _e2p_upgrade_0_4_5 (); //suggest default } gboolean init_plugin (Plugin *p) { #define ANAME "uprade" p->signature = ANAME VERSION; if (strcmp (app.cfgfile_version,"0.1") < 0) //this one's major - dump the lot and start afresh _e2p_upgrade_pre_0_1(); else { //NOTE ensure that config plugin uses this same definition #define OLDEST_UPGRADE "0.4.1.3" if (strcmp (app.cfgfile_version,"0.5.0") < 0) { _e2p_upgrade_0_4_1 (); //change the find-plugin ABI _e2p_upgrade_0_4_5 (); } else { #ifdef E2_VERSIONDOCS _e2p_upgrade_numbers (); //always update docs version etc #endif #ifdef E2_POLKIT //this #define can be changed at any time cancelled = !_e2p_upgrade_su (); #else cancelled = TRUE; #endif if (strcmp (app.cfgfile_version,"0.5.1") < 0) { cancelled = FALSE; //may be changed downstream _e2p_upgrade_0_5_1 (); } if (strcmp (app.cfgfile_version,"0.5.1.1") < 0) { cancelled = FALSE; _e2p_upgrade_0_5_1_1 (); } if (strcmp (app.cfgfile_version,"0.7.2") < 0) { cancelled = FALSE; _e2p_upgrade_0_7_2 (); } } if (!cancelled) _e2p_upgrade_reconfig (); //implement all updates //after any config-file-based updates, work on stored data ... if (strcmp (app.cfgfile_version,"0.6.0") < 0) _e2p_upgrade_0_6_0 (); } return TRUE; } gboolean clean_plugin (Plugin *p) { return TRUE; }