summaryrefslogtreecommitdiff
path: root/apps/plugins/theme_remove.c
diff options
context:
space:
mode:
authorTeruaki Kawashima <teru@rockbox.org>2009-10-12 13:55:59 +0000
committerTeruaki Kawashima <teru@rockbox.org>2009-10-12 13:55:59 +0000
commit4d900bd786b37a2d9a5bdff0d557c667d6d897ba (patch)
treec481ab7a733ad2c6f5617a554afa56e208c0093c /apps/plugins/theme_remove.c
parent7a5904cf1c7fbe27bdb383322d0c45eebb401f33 (diff)
New plugin theme_remove which offers a way to remove specified theme.
See the manual for usage. Flyspray: FS#10187 Author: myself, manual by David Kauffmann git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23130 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/theme_remove.c')
-rw-r--r--apps/plugins/theme_remove.c702
1 files changed, 702 insertions, 0 deletions
diff --git a/apps/plugins/theme_remove.c b/apps/plugins/theme_remove.c
new file mode 100644
index 0000000000..a2e7bf21a5
--- /dev/null
+++ b/apps/plugins/theme_remove.c
@@ -0,0 +1,702 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lib/configfile.h"
+
+PLUGIN_HEADER
+
+/* taken from apps/gui/wps_parser.c */
+#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
+#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
+
+#define CONFIG_FILENAME "theme_remove.cfg"
+#define LOG_FILENAME "/theme_remove_log.txt"
+#define RB_FONTS_CONFIG VIEWERS_DIR "/rockbox-fonts.config"
+
+enum remove_option {
+ ALWAYS_REMOVE,
+ NEVER_REMOVE,
+ REMOVE_IF_NOT_USED,
+ ASK_FOR_REMOVAL,
+ NUM_REMOVE_OPTION
+};
+
+struct remove_setting {
+ const char *name;
+ const char *prefix, *suffix;
+ char value[MAX_PATH];
+ int option;
+ int (*func)(struct remove_setting *);
+ bool used;
+};
+
+static int remove_wps(struct remove_setting *);
+#ifdef HAVE_LCD_BITMAP
+static int remove_icons(struct remove_setting *setting);
+#endif
+
+enum remove_settings {
+#ifdef HAVE_LCD_BITMAP
+ REMOVE_FONT,
+#endif
+ REMOVE_WPS,
+#ifdef HAVE_REMOTE_LCD
+ REMOVE_RWPS,
+#endif
+#if LCD_DEPTH > 1
+ REMOVE_BACKDROP,
+#endif
+#ifdef HAVE_LCD_BITMAP
+ REMOVE_ICON,
+ REMOVE_VICON,
+#endif
+#ifdef HAVE_REMOTE_LCD
+ REMOVE_RICON,
+ REMOVE_RVICON,
+#endif
+#ifdef HAVE_LCD_COLOR
+ REMOVE_COLOURS,
+#endif
+ NUM_REMOVE_ITEMS
+};
+
+static bool create_log = true;
+static struct remove_setting remove_list[NUM_REMOVE_ITEMS] = {
+#ifdef HAVE_LCD_BITMAP
+ [REMOVE_FONT] = { "font", FONT_DIR "/", ".fnt", "",
+ ASK_FOR_REMOVAL, NULL, false },
+#endif
+ [REMOVE_WPS] = { "wps", WPS_DIR "/", ".wps", "",
+ REMOVE_IF_NOT_USED, remove_wps, false },
+#ifdef HAVE_REMOTE_LCD
+ [REMOVE_RWPS] = { "rwps", WPS_DIR "/", ".rwps", "",
+ REMOVE_IF_NOT_USED, remove_wps, false },
+#endif
+#if LCD_DEPTH > 1
+ [REMOVE_BACKDROP] = { "backdrop", BACKDROP_DIR "/", ".bmp", "",
+ REMOVE_IF_NOT_USED, NULL, false },
+#endif
+#ifdef HAVE_LCD_BITMAP
+ [REMOVE_ICON] = { "iconset", ICON_DIR "/", ".bmp", "",
+ ASK_FOR_REMOVAL, NULL, false },
+ [REMOVE_VICON] = { "viewers iconset", ICON_DIR "/", ".bmp", "",
+ ASK_FOR_REMOVAL, remove_icons, false },
+#endif
+#ifdef HAVE_REMOTE_LCD
+ [REMOVE_RICON] = { "remote iconset", ICON_DIR "/", ".bmp", "",
+ ASK_FOR_REMOVAL, NULL, false },
+ [REMOVE_RVICON] = { "remote viewers iconset", ICON_DIR "/", ".bmp", "",
+ ASK_FOR_REMOVAL, NULL, false },
+#endif
+#ifdef HAVE_LCD_COLOR
+ [REMOVE_COLOURS] = { "filetype colours", THEME_DIR "/", ".colours", "",
+ ASK_FOR_REMOVAL, NULL, false },
+#endif
+};
+static char *option_names[NUM_REMOVE_OPTION] = {
+ "always", "never", "not used", "ask",
+};
+static struct configdata config[] = {
+#ifdef HAVE_LCD_BITMAP
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_FONT].option },
+ "remove font", option_names },
+#endif
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_WPS].option },
+ "remove wps", option_names },
+#ifdef HAVE_REMOTE_LCD
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_RWPS].option },
+ "remove rwps", option_names },
+#endif
+#if LCD_DEPTH > 1
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_BACKDROP].option },
+ "remove backdrop", option_names },
+#endif
+#ifdef HAVE_LCD_BITMAP
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_ICON].option },
+ "remove iconset", option_names },
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_VICON].option },
+ "remove viconset", option_names },
+#endif
+#ifdef HAVE_REMOTE_LCD
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_RICON].option },
+ "remove riconset", option_names },
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_RVICON].option },
+ "remove rviconset", option_names },
+#endif
+#ifdef HAVE_LCD_COLOR
+ { TYPE_INT, 0, NUM_REMOVE_OPTION,
+ { .int_p = &remove_list[REMOVE_COLOURS].option },
+ "remove colours", option_names },
+#endif
+ {TYPE_BOOL, 0, 1, { .bool_p = &create_log },
+ "create log", NULL},
+};
+static const int nb_config = sizeof(config)/sizeof(*config);
+static char themefile[MAX_PATH];
+static int log_fd = -1;
+
+static int show_mess(const char *text, const char *file)
+{
+ static char buf[MAX_PATH*2];
+
+ if (file)
+ rb->snprintf(buf, sizeof(buf), "%s: %s", text, file);
+ else
+ rb->snprintf(buf, sizeof(buf), "%s", text);
+
+ DEBUGF("%s\n", buf);
+ if (log_fd >= 0)
+ rb->fdprintf(log_fd, "%s\n", buf);
+
+ rb->splash(0, buf);
+ rb->sleep(HZ/4);
+
+ return 0;
+}
+
+/* set full path of file. */
+static void set_file_name(char *buf, const char*file,
+ struct remove_setting *setting)
+{
+ int len1, len2;
+ if (rb->strncasecmp(file, setting->prefix, rb->strlen(setting->prefix)))
+ rb->snprintf(buf, MAX_PATH, "%s%s", setting->prefix, file);
+ else
+ rb->strlcpy(buf, file, MAX_PATH);
+ len1 = rb->strlen(buf);
+ len2 = rb->strlen(setting->suffix);
+ if (rb->strcasecmp(buf+len1-len2, setting->suffix))
+ rb->strlcpy(&buf[len1], setting->suffix, MAX_PATH-len1);
+}
+
+/* taken from apps/onplay.c */
+/* helper function to remove a non-empty directory */
+static int remove_dir(char* dirname, int len)
+{
+ int result = 0;
+ DIR* dir;
+ int dirlen = rb->strlen(dirname);
+
+ dir = rb->opendir(dirname);
+ if (!dir)
+ return -1; /* open error */
+
+ while (true)
+ {
+ struct dirent* entry;
+ /* walk through the directory content */
+ entry = rb->readdir(dir);
+ if (!entry)
+ break;
+
+ dirname[dirlen] ='\0';
+
+ /* append name to current directory */
+ rb->snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name);
+ if (entry->attribute & ATTR_DIRECTORY)
+ {
+ /* remove a subdirectory */
+ if (!rb->strcmp((char *)entry->d_name, ".") ||
+ !rb->strcmp((char *)entry->d_name, ".."))
+ continue; /* skip these */
+
+ result = remove_dir(dirname, len); /* recursion */
+ if (result)
+ break;
+ }
+ else
+ {
+ /* remove a file */
+ result = rb->remove(dirname);
+ }
+ if (ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK))
+ {
+ show_mess("Canceled", NULL);
+ result = -1;
+ break;
+ }
+ }
+ rb->closedir(dir);
+
+ if (!result)
+ { /* remove the now empty directory */
+ dirname[dirlen] = '\0'; /* terminate to original length */
+
+ result = rb->rmdir(dirname);
+ show_mess("Removed", dirname);
+ }
+
+ return result;
+}
+
+static int remove_wps(struct remove_setting *setting)
+{
+ char bmpdir[MAX_PATH];
+ char *p;
+ rb->strcpy(bmpdir, setting->value);
+ p = rb->strrchr(bmpdir, '.');
+ if (p) *p = 0;
+ if (!rb->dir_exists(bmpdir))
+ return 0;
+ return remove_dir(bmpdir, MAX_PATH);
+}
+
+#ifdef HAVE_LCD_BITMAP
+static int remove_icons(struct remove_setting *setting)
+{
+ char path[MAX_PATH];
+ char *p;
+ rb->strcpy(path, setting->value);
+ p = rb->strrchr(path, '.');
+ rb->strlcpy(p, ".icons", path+MAX_PATH-p);
+
+ if (!rb->file_exists(path))
+ {
+ return 0;
+ }
+ if (rb->remove(path))
+ {
+ show_mess("Failed", path);
+ return 1;
+ }
+ show_mess("Removed", path);
+ return 0;
+}
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+static char font_file[MAX_PATH];
+#endif
+static bool is_deny_file(const char *file)
+{
+ const char *deny_files[] = {
+ WPS_DEFAULTCFG,
+ RWPS_DEFAULTCFG,
+#ifdef HAVE_LCD_BITMAP
+ font_file,
+#endif
+ NULL
+ };
+ const char **p = deny_files;
+ while ( *p )
+ {
+ if (!rb->strcmp(file, *p))
+ return true;
+ p++;
+ }
+ return false;
+}
+
+static void check_whether_used_in_setting(void)
+{
+ const char *setting_files[] = {
+#ifdef HAVE_LCD_BITMAP
+ rb->global_settings->font_file,
+#endif
+ rb->global_settings->wps_file,
+#ifdef HAVE_REMOTE_LCD
+ rb->global_settings->rwps_file,
+#endif
+#if LCD_DEPTH > 1
+ rb->global_settings->backdrop_file,
+#endif
+#ifdef HAVE_LCD_BITMAP
+ rb->global_settings->icon_file,
+ rb->global_settings->viewers_icon_file,
+#endif
+#ifdef HAVE_REMOTE_LCD
+ rb->global_settings->remote_icon_file,
+ rb->global_settings->remote_viewers_icon_file,
+#endif
+#ifdef HAVE_LCD_COLOR
+ rb->global_settings->colors_file,
+#endif
+ };
+ char tempfile[MAX_PATH];
+ int i;
+ for (i=0; i<NUM_REMOVE_ITEMS; i++)
+ {
+ struct remove_setting *setting = &remove_list[i];
+ if (setting->value[0])
+ {
+ set_file_name(tempfile, setting_files[i], setting);
+ if (!rb->strcasecmp(tempfile, setting->value))
+ setting->used = true;
+ }
+ }
+}
+static void check_whether_used_in_file(const char *cfgfile)
+{
+ char line[MAX_PATH];
+ char settingfile[MAX_PATH];
+ char *p;
+ int fd;
+ char *name, *value;
+ int i;
+
+ if (!rb->strcasecmp(themefile, cfgfile))
+ return;
+ fd = rb->open(cfgfile, O_RDONLY);
+ if (fd < 0)
+ return;
+ while (rb->read_line(fd, line, sizeof(line)) > 0)
+ {
+ if (!rb->settings_parseline(line, &name, &value))
+ continue;
+ /* remove trailing spaces. */
+ p = value+rb->strlen(value)-1;
+ while (*p == ' ') *p-- = 0;
+ if (*value == 0 || !rb->strcmp(value, "-"))
+ continue;
+ for (i=0; i<NUM_REMOVE_ITEMS; i++)
+ {
+ struct remove_setting *setting = &remove_list[i];
+ if (!rb->strcmp(name, setting->name))
+ {
+ if (setting->value[0])
+ {
+ set_file_name(settingfile, value, setting);
+ if (!rb->strcasecmp(settingfile, setting->value))
+ setting->used = true;
+ }
+ break;
+ }
+ }
+ }
+ rb->close(fd);
+}
+static void check_whether_used(void)
+{
+ char cfgfile[MAX_PATH];
+ DIR *dir;
+
+ check_whether_used_in_setting();
+#ifdef HAVE_LCD_BITMAP
+ /* mark font files come from rockbox-font.zip as used and don't remove
+ * them automatically as themes may depend on those fonts. */
+ if (remove_list[REMOVE_FONT].option == REMOVE_IF_NOT_USED)
+ check_whether_used_in_file(RB_FONTS_CONFIG);
+#endif
+
+ dir = rb->opendir(THEME_DIR);
+ if (!dir)
+ return; /* open error */
+
+ while (true)
+ {
+ struct dirent* entry;
+ char *p;
+ int i;
+ /* walk through the directory content */
+ entry = rb->readdir(dir);
+ if (!entry)
+ break;
+ p = rb->strrchr(entry->d_name, '.');
+ if (!p || rb->strcmp(p, ".cfg"))
+ continue;
+
+ rb->snprintf(cfgfile, MAX_PATH, "%s/%s", THEME_DIR, entry->d_name);
+ check_whether_used_in_file(cfgfile);
+ /* break the loop if all files need to be checked in the theme
+ * turned out to be used. */
+ for (i = 0; i < NUM_REMOVE_ITEMS; i++)
+ {
+ struct remove_setting *setting = &remove_list[i];
+ if (setting->option == REMOVE_IF_NOT_USED)
+ {
+ if (setting->value[0] && !setting->used)
+ break;
+ }
+ }
+ if (i == NUM_REMOVE_ITEMS)
+ break;
+ }
+ rb->closedir(dir);
+}
+
+static int remove_file(struct remove_setting *setting)
+{
+ if (!rb->file_exists(setting->value))
+ {
+ show_mess("Doesn't exist", setting->value);
+ return 0;
+ }
+ if (is_deny_file(setting->value))
+ {
+ show_mess("Denied", setting->value);
+ return 0;
+ }
+ switch (setting->option)
+ {
+ case ALWAYS_REMOVE:
+ break;
+ case NEVER_REMOVE:
+ show_mess("Skipped", setting->value);
+ return 0;
+ break;
+ case REMOVE_IF_NOT_USED:
+ if (setting->used)
+ {
+ show_mess("Used", setting->value);
+ return 0;
+ }
+ break;
+ case ASK_FOR_REMOVAL:
+ default:
+ {
+ const char *message_lines[] = { "Delete?", setting->value };
+ const struct text_message text_message = { message_lines, 2 };
+ if (rb->gui_syncyesno_run(&text_message, NULL, NULL) != YESNO_YES)
+ {
+ show_mess("Skipped", setting->value);
+ return 0;
+ }
+ }
+ break;
+ }
+ if (rb->remove(setting->value))
+ {
+ show_mess("Failed", setting->value);
+ return -1;
+ }
+ if (setting->func && setting->func(setting))
+ return -1;
+ show_mess("Removed", setting->value);
+ return 1;
+}
+static int remove_theme(void)
+{
+ static char line[MAX_PATH];
+ int fd;
+ int i, num_removed = 0;
+ char *name, *value;
+ bool needs_to_check_whether_used = false;
+
+ /* initialize for safe */
+ for (i=0; i<NUM_REMOVE_ITEMS; i++)
+ remove_list[i].value[0] = 0;
+
+ /* load settings */
+ fd = rb->open(themefile, O_RDONLY);
+ if (fd < 0) return fd;
+ while (rb->read_line(fd, line, sizeof(line)) > 0)
+ {
+ if (!rb->settings_parseline(line, &name, &value))
+ continue;
+ /* remove trailing spaces. */
+ char *p = value+rb->strlen(value)-1;
+ while (*p == ' ') *p-- = 0;
+ if (*value == 0 || !rb->strcmp(value, "-"))
+ continue;
+ for (i=0; i<NUM_REMOVE_ITEMS; i++)
+ {
+ struct remove_setting *setting = &remove_list[i];
+ if (!rb->strcmp(name, setting->name))
+ {
+ set_file_name(setting->value, value, setting);
+ if(setting->option == REMOVE_IF_NOT_USED)
+ needs_to_check_whether_used = true;
+ break;
+ }
+ }
+ }
+ rb->close(fd);
+
+ if(needs_to_check_whether_used)
+ check_whether_used();
+
+ /* now remove file assosiated to the theme. */
+ for (i=0; i<NUM_REMOVE_ITEMS; i++)
+ {
+ if (remove_list[i].value[0])
+ {
+ int ret = remove_file(&remove_list[i]);
+ if (ret < 0)
+ return ret;
+ num_removed += ret;
+ }
+ }
+
+ /* remove the setting file iff it is in theme directory to protect
+ * aginst accidental removal of non theme cfg file. if the file is
+ * not in the theme directory, the file may not be a theme cfg file. */
+ if (rb->strncasecmp(themefile, THEME_DIR "/", sizeof(THEME_DIR "/")-1))
+ {
+ show_mess("Skipped", themefile);
+ }
+ else if (rb->remove(themefile))
+ {
+ show_mess("Failed", themefile);
+ return -1;
+ }
+ else
+ {
+ show_mess("Removed", themefile);
+ rb->reload_directory();
+ num_removed++;
+ }
+ return num_removed;
+}
+
+static bool option_changed = false;
+static bool option_menu(void)
+{
+ MENUITEM_STRINGLIST(option_menu, "Remove Options", NULL,
+ /* same order as remove_list */
+#ifdef HAVE_LCD_BITMAP
+ "Font",
+#endif
+ "WPS",
+#ifdef HAVE_REMOTE_LCD
+ "Remote WPS",
+#endif
+#if LCD_DEPTH > 1
+ "Backdrop",
+#endif
+#ifdef HAVE_LCD_BITMAP
+ "Iconset", "Viewers Iconset",
+#endif
+#ifdef HAVE_REMOTE_LCD
+ "Remote Iconset", "Remote Viewers Iconset",
+#endif
+#ifdef HAVE_LCD_COLOR
+ "Filetype Colours",
+#endif
+ "Create Log File");
+ struct opt_items remove_names[] = {
+ {"Always Remove", -1}, {"Never Remove", -1},
+ {"Remove if not Used", -1}, {"Ask for Removal", -1},
+ };
+ int selected = 0, result;
+
+ while (1)
+ {
+ result = rb->do_menu(&option_menu, &selected, NULL, false);
+ if (result >= 0 && result < NUM_REMOVE_ITEMS)
+ {
+ struct remove_setting *setting = &remove_list[result];
+ int prev_option = setting->option;
+ if (rb->set_option(option_menu_[result], &setting->option, INT,
+ remove_names, NUM_REMOVE_OPTION, NULL))
+ return true;
+ if (prev_option != setting->option)
+ option_changed = true;
+ }
+ else if (result == NUM_REMOVE_ITEMS)
+ {
+ bool prev_value = create_log;
+ if(rb->set_bool("Create Log File", &create_log))
+ return true;
+ if (prev_value != create_log)
+ option_changed = true;
+ }
+ else if (result == MENU_ATTACHED_USB)
+ return true;
+ else
+ return false;
+ }
+
+ return false;
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ static char title[64];
+ char *p;
+ MENUITEM_STRINGLIST(menu, title, NULL,
+ "Remove Theme", "Remove Options",
+ "Quit");
+ int selected = 0, ret;
+ bool exit = false;
+
+ if (!parameter)
+ return PLUGIN_ERROR;
+
+ rb->snprintf(title, sizeof(title), "Remove %s",
+ rb->strrchr(parameter, '/')+1);
+ if((p = rb->strrchr(title, '.')))
+ *p = 0;
+
+#ifdef HAVE_LCD_BITMAP
+ rb->snprintf(font_file, MAX_PATH, FONT_DIR "/%s.fnt",
+ rb->global_settings->font_file);
+#endif
+ rb->strlcpy(themefile, parameter, MAX_PATH);
+ if (!rb->file_exists(themefile))
+ {
+ rb->splash(HZ, "File open error!");
+ return PLUGIN_ERROR;
+ }
+ configfile_load(CONFIG_FILENAME, config, nb_config, 0);
+ while (!exit)
+ {
+ switch (rb->do_menu(&menu, &selected, NULL, false))
+ {
+ case 0:
+ if(create_log)
+ {
+ log_fd = rb->open(LOG_FILENAME, O_WRONLY|O_CREAT|O_APPEND);
+ if(log_fd >= 0)
+ rb->fdprintf(log_fd, "---- %s ----\n", title);
+ else
+ show_mess("Couldn't open log file.", NULL);
+ }
+ ret = remove_theme();
+ p = (ret >= 0? "Successfully removed!": "Remove failure");
+ show_mess(p, NULL);
+ if(log_fd >= 0)
+ {
+ rb->fdprintf(log_fd, "----------------\n");
+ rb->close(log_fd);
+ log_fd = -1;
+ }
+ rb->lcd_clear_display();
+ rb->lcd_update();
+ rb->splashf(0, "%s %s", p, "Press any key to exit.");
+ rb->button_clear_queue();
+ rb->button_get(true);
+ exit = true;
+ break;
+ case 1:
+ if (option_menu())
+ return PLUGIN_USB_CONNECTED;
+ break;
+ case 2:
+ exit = true;
+ break;
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ break;
+ default:
+ break;
+ }
+ }
+ if(option_changed)
+ configfile_save(CONFIG_FILENAME, config, nb_config, 0);
+ return PLUGIN_OK;
+}