Prepare new maemo release
[maemo-rb.git] / apps / plugins / theme_remove.c
blob674342a92065071df8559ac68ed065edb36176fc
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "plugin.h"
21 #include "lib/configfile.h"
25 /* taken from apps/gui/wps_parser.c */
26 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
27 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
29 #define CONFIG_FILENAME "theme_remove.cfg"
30 #define LOG_FILENAME "/theme_remove_log.txt"
31 #define RB_FONTS_CONFIG VIEWERS_DIR "/rockbox-fonts.config"
33 enum remove_option {
34 ALWAYS_REMOVE,
35 NEVER_REMOVE,
36 REMOVE_IF_NOT_USED,
37 ASK_FOR_REMOVAL,
38 NUM_REMOVE_OPTION
41 struct remove_setting {
42 const char *name;
43 const char *prefix, *suffix;
44 char value[MAX_PATH];
45 int option;
46 int (*func)(struct remove_setting *);
47 bool used;
50 static int remove_wps(struct remove_setting *);
51 #ifdef HAVE_LCD_BITMAP
52 static int remove_icons(struct remove_setting *setting);
53 #endif
55 enum remove_settings {
56 #ifdef HAVE_LCD_BITMAP
57 REMOVE_FONT,
58 #endif
59 REMOVE_WPS,
60 #ifdef HAVE_LCD_BITMAP
61 REMOVE_SBS,
62 #endif
63 #ifdef HAVE_REMOTE_LCD
64 REMOVE_RWPS,
65 REMOVE_RSBS,
66 #endif
67 #if LCD_DEPTH > 1
68 REMOVE_BACKDROP,
69 #endif
70 #ifdef HAVE_LCD_BITMAP
71 REMOVE_ICON,
72 REMOVE_VICON,
73 #endif
74 #ifdef HAVE_REMOTE_LCD
75 REMOVE_RICON,
76 REMOVE_RVICON,
77 #endif
78 #ifdef HAVE_LCD_COLOR
79 REMOVE_COLOURS,
80 #endif
81 NUM_REMOVE_ITEMS
84 static bool create_log = true;
85 static struct remove_setting remove_list[NUM_REMOVE_ITEMS] = {
86 #ifdef HAVE_LCD_BITMAP
87 [REMOVE_FONT] = { "font", FONT_DIR "/", ".fnt", "",
88 ASK_FOR_REMOVAL, NULL, false },
89 #endif
90 [REMOVE_WPS] = { "wps", WPS_DIR "/", ".wps", "",
91 REMOVE_IF_NOT_USED, remove_wps, false },
92 #ifdef HAVE_LCD_BITMAP
93 [REMOVE_SBS] = { "sbs", SBS_DIR "/", ".sbs", "",
94 REMOVE_IF_NOT_USED, remove_wps, false },
95 #endif
96 #ifdef HAVE_REMOTE_LCD
97 [REMOVE_RWPS] = { "rwps", WPS_DIR "/", ".rwps", "",
98 REMOVE_IF_NOT_USED, remove_wps, false },
99 [REMOVE_RSBS] = { "rsbs", SBS_DIR "/", ".rsbs", "",
100 REMOVE_IF_NOT_USED, remove_wps, false },
101 #endif
102 #if LCD_DEPTH > 1
103 [REMOVE_BACKDROP] = { "backdrop", BACKDROP_DIR "/", ".bmp", "",
104 REMOVE_IF_NOT_USED, NULL, false },
105 #endif
106 #ifdef HAVE_LCD_BITMAP
107 [REMOVE_ICON] = { "iconset", ICON_DIR "/", ".bmp", "",
108 ASK_FOR_REMOVAL, NULL, false },
109 [REMOVE_VICON] = { "viewers iconset", ICON_DIR "/", ".bmp", "",
110 ASK_FOR_REMOVAL, remove_icons, false },
111 #endif
112 #ifdef HAVE_REMOTE_LCD
113 [REMOVE_RICON] = { "remote iconset", ICON_DIR "/", ".bmp", "",
114 ASK_FOR_REMOVAL, NULL, false },
115 [REMOVE_RVICON] = { "remote viewers iconset", ICON_DIR "/", ".bmp", "",
116 ASK_FOR_REMOVAL, NULL, false },
117 #endif
118 #ifdef HAVE_LCD_COLOR
119 [REMOVE_COLOURS] = { "filetype colours", THEME_DIR "/", ".colours", "",
120 ASK_FOR_REMOVAL, NULL, false },
121 #endif
123 static char *option_names[NUM_REMOVE_OPTION] = {
124 "always", "never", "not used", "ask",
126 static struct configdata config[] = {
127 #ifdef HAVE_LCD_BITMAP
128 { TYPE_INT, 0, NUM_REMOVE_OPTION,
129 { .int_p = &remove_list[REMOVE_FONT].option },
130 "remove font", option_names },
131 #endif
132 { TYPE_INT, 0, NUM_REMOVE_OPTION,
133 { .int_p = &remove_list[REMOVE_WPS].option },
134 "remove wps", option_names },
135 #ifdef HAVE_LCD_BITMAP
136 { TYPE_INT, 0, NUM_REMOVE_OPTION,
137 { .int_p = &remove_list[REMOVE_SBS].option },
138 "remove sbs", option_names },
139 #endif
140 #ifdef HAVE_REMOTE_LCD
141 { TYPE_INT, 0, NUM_REMOVE_OPTION,
142 { .int_p = &remove_list[REMOVE_RWPS].option },
143 "remove rwps", option_names },
144 { TYPE_INT, 0, NUM_REMOVE_OPTION,
145 { .int_p = &remove_list[REMOVE_RSBS].option },
146 "remove rsbs", option_names },
147 #endif
148 #if LCD_DEPTH > 1
149 { TYPE_INT, 0, NUM_REMOVE_OPTION,
150 { .int_p = &remove_list[REMOVE_BACKDROP].option },
151 "remove backdrop", option_names },
152 #endif
153 #ifdef HAVE_LCD_BITMAP
154 { TYPE_INT, 0, NUM_REMOVE_OPTION,
155 { .int_p = &remove_list[REMOVE_ICON].option },
156 "remove iconset", option_names },
157 { TYPE_INT, 0, NUM_REMOVE_OPTION,
158 { .int_p = &remove_list[REMOVE_VICON].option },
159 "remove viconset", option_names },
160 #endif
161 #ifdef HAVE_REMOTE_LCD
162 { TYPE_INT, 0, NUM_REMOVE_OPTION,
163 { .int_p = &remove_list[REMOVE_RICON].option },
164 "remove riconset", option_names },
165 { TYPE_INT, 0, NUM_REMOVE_OPTION,
166 { .int_p = &remove_list[REMOVE_RVICON].option },
167 "remove rviconset", option_names },
168 #endif
169 #ifdef HAVE_LCD_COLOR
170 { TYPE_INT, 0, NUM_REMOVE_OPTION,
171 { .int_p = &remove_list[REMOVE_COLOURS].option },
172 "remove colours", option_names },
173 #endif
174 {TYPE_BOOL, 0, 1, { .bool_p = &create_log },
175 "create log", NULL},
177 static const int nb_config = sizeof(config)/sizeof(*config);
178 static char themefile[MAX_PATH];
179 static int log_fd = -1;
181 static int show_mess(const char *text, const char *file)
183 static char buf[MAX_PATH*2];
185 if (file)
186 rb->snprintf(buf, sizeof(buf), "%s: %s", text, file);
187 else
188 rb->snprintf(buf, sizeof(buf), "%s", text);
190 DEBUGF("%s\n", buf);
191 if (log_fd >= 0)
192 rb->fdprintf(log_fd, "%s\n", buf);
194 rb->splash(0, buf);
195 rb->sleep(HZ/4);
197 return 0;
200 /* set full path of file. */
201 static void set_file_name(char *buf, const char*file,
202 struct remove_setting *setting)
204 int len1, len2;
205 if (rb->strncasecmp(file, setting->prefix, rb->strlen(setting->prefix)))
206 rb->snprintf(buf, MAX_PATH, "%s%s", setting->prefix, file);
207 else
208 rb->strlcpy(buf, file, MAX_PATH);
209 len1 = rb->strlen(buf);
210 len2 = rb->strlen(setting->suffix);
211 if (rb->strcasecmp(buf+len1-len2, setting->suffix))
212 rb->strlcpy(&buf[len1], setting->suffix, MAX_PATH-len1);
215 /* taken from apps/onplay.c */
216 /* helper function to remove a non-empty directory */
217 static int remove_dir(char* dirname, int len)
219 int result = 0;
220 DIR* dir;
221 int dirlen = rb->strlen(dirname);
223 dir = rb->opendir(dirname);
224 if (!dir)
225 return -1; /* open error */
227 while (true)
229 struct dirent* entry;
230 /* walk through the directory content */
231 entry = rb->readdir(dir);
232 if (!entry)
233 break;
235 dirname[dirlen] ='\0';
237 /* append name to current directory */
238 rb->snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name);
239 struct dirinfo info = rb->dir_get_info(dir, entry);
240 if (info.attribute & ATTR_DIRECTORY)
242 /* remove a subdirectory */
243 if (!rb->strcmp((char *)entry->d_name, ".") ||
244 !rb->strcmp((char *)entry->d_name, ".."))
245 continue; /* skip these */
247 result = remove_dir(dirname, len); /* recursion */
248 if (result)
249 break;
251 else
253 /* remove a file */
254 result = rb->remove(dirname);
256 if (ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK))
258 show_mess("Canceled", NULL);
259 result = -1;
260 break;
263 rb->closedir(dir);
265 if (!result)
266 { /* remove the now empty directory */
267 dirname[dirlen] = '\0'; /* terminate to original length */
269 result = rb->rmdir(dirname);
270 show_mess("Removed", dirname);
273 return result;
276 static int remove_wps(struct remove_setting *setting)
278 char bmpdir[MAX_PATH];
279 char *p;
280 rb->strcpy(bmpdir, setting->value);
281 p = rb->strrchr(bmpdir, '.');
282 if (p) *p = 0;
283 if (!rb->dir_exists(bmpdir))
284 return 0;
285 return remove_dir(bmpdir, MAX_PATH);
288 #ifdef HAVE_LCD_BITMAP
289 static int remove_icons(struct remove_setting *setting)
291 char path[MAX_PATH];
292 char *p;
293 rb->strcpy(path, setting->value);
294 p = rb->strrchr(path, '.');
295 rb->strlcpy(p, ".icons", path+MAX_PATH-p);
297 if (!rb->file_exists(path))
299 return 0;
301 if (rb->remove(path))
303 show_mess("Failed", path);
304 return 1;
306 show_mess("Removed", path);
307 return 0;
309 #endif
311 #ifdef HAVE_LCD_BITMAP
312 static char font_file[MAX_PATH];
313 #endif
314 static bool is_deny_file(const char *file)
316 const char *deny_files[] = {
317 WPS_DEFAULTCFG,
318 RWPS_DEFAULTCFG,
319 #ifdef HAVE_LCD_BITMAP
320 font_file,
321 #endif
322 NULL
324 const char **p = deny_files;
325 while ( *p )
327 if (!rb->strcmp(file, *p))
328 return true;
329 p++;
331 return false;
334 static void check_whether_used_in_setting(void)
336 const char *setting_files[] = {
337 #ifdef HAVE_LCD_BITMAP
338 rb->global_settings->font_file,
339 #endif
340 rb->global_settings->wps_file,
341 #ifdef HAVE_LCD_BITMAP
342 rb->global_settings->sbs_file,
343 #endif
344 #ifdef HAVE_REMOTE_LCD
345 rb->global_settings->rwps_file,
346 rb->global_settings->rsbs_file,
347 #endif
348 #if LCD_DEPTH > 1
349 rb->global_settings->backdrop_file,
350 #endif
351 #ifdef HAVE_LCD_BITMAP
352 rb->global_settings->icon_file,
353 rb->global_settings->viewers_icon_file,
354 #endif
355 #ifdef HAVE_REMOTE_LCD
356 rb->global_settings->remote_icon_file,
357 rb->global_settings->remote_viewers_icon_file,
358 #endif
359 #ifdef HAVE_LCD_COLOR
360 rb->global_settings->colors_file,
361 #endif
363 char tempfile[MAX_PATH];
364 int i;
365 for (i=0; i<NUM_REMOVE_ITEMS; i++)
367 struct remove_setting *setting = &remove_list[i];
368 if (setting->value[0])
370 set_file_name(tempfile, setting_files[i], setting);
371 if (!rb->strcasecmp(tempfile, setting->value))
372 setting->used = true;
376 static void check_whether_used_in_file(const char *cfgfile)
378 char line[MAX_PATH];
379 char settingfile[MAX_PATH];
380 char *p;
381 int fd;
382 char *name, *value;
383 int i;
385 if (!rb->strcasecmp(themefile, cfgfile))
386 return;
387 fd = rb->open(cfgfile, O_RDONLY);
388 if (fd < 0)
389 return;
390 while (rb->read_line(fd, line, sizeof(line)) > 0)
392 if (!rb->settings_parseline(line, &name, &value))
393 continue;
394 /* remove trailing spaces. */
395 p = value+rb->strlen(value)-1;
396 while (*p == ' ') *p-- = 0;
397 if (*value == 0 || !rb->strcmp(value, "-"))
398 continue;
399 for (i=0; i<NUM_REMOVE_ITEMS; i++)
401 struct remove_setting *setting = &remove_list[i];
402 if (!rb->strcmp(name, setting->name))
404 if (setting->value[0])
406 set_file_name(settingfile, value, setting);
407 if (!rb->strcasecmp(settingfile, setting->value))
408 setting->used = true;
410 break;
414 rb->close(fd);
416 static void check_whether_used(void)
418 char cfgfile[MAX_PATH];
419 DIR *dir;
421 check_whether_used_in_setting();
422 #ifdef HAVE_LCD_BITMAP
423 /* mark font files come from rockbox-font.zip as used and don't remove
424 * them automatically as themes may depend on those fonts. */
425 if (remove_list[REMOVE_FONT].option == REMOVE_IF_NOT_USED)
426 check_whether_used_in_file(RB_FONTS_CONFIG);
427 #endif
429 dir = rb->opendir(THEME_DIR);
430 if (!dir)
431 return; /* open error */
433 while (true)
435 struct dirent* entry;
436 char *p;
437 int i;
438 /* walk through the directory content */
439 entry = rb->readdir(dir);
440 if (!entry)
441 break;
442 p = rb->strrchr(entry->d_name, '.');
443 if (!p || rb->strcmp(p, ".cfg"))
444 continue;
446 rb->snprintf(cfgfile, MAX_PATH, "%s/%s", THEME_DIR, entry->d_name);
447 check_whether_used_in_file(cfgfile);
448 /* break the loop if all files need to be checked in the theme
449 * turned out to be used. */
450 for (i = 0; i < NUM_REMOVE_ITEMS; i++)
452 struct remove_setting *setting = &remove_list[i];
453 if (setting->option == REMOVE_IF_NOT_USED)
455 if (setting->value[0] && !setting->used)
456 break;
459 if (i == NUM_REMOVE_ITEMS)
460 break;
462 rb->closedir(dir);
465 static int remove_file(struct remove_setting *setting)
467 if (!rb->file_exists(setting->value))
469 show_mess("Doesn't exist", setting->value);
470 return 0;
472 if (is_deny_file(setting->value))
474 show_mess("Denied", setting->value);
475 return 0;
477 switch (setting->option)
479 case ALWAYS_REMOVE:
480 break;
481 case NEVER_REMOVE:
482 show_mess("Skipped", setting->value);
483 return 0;
484 break;
485 case REMOVE_IF_NOT_USED:
486 if (setting->used)
488 show_mess("Used", setting->value);
489 return 0;
491 break;
492 case ASK_FOR_REMOVAL:
493 default:
495 const char *message_lines[] = { "Delete?", setting->value };
496 const struct text_message text_message = { message_lines, 2 };
497 if (rb->gui_syncyesno_run(&text_message, NULL, NULL) != YESNO_YES)
499 show_mess("Skipped", setting->value);
500 return 0;
503 break;
505 if (rb->remove(setting->value))
507 show_mess("Failed", setting->value);
508 return -1;
510 if (setting->func && setting->func(setting))
511 return -1;
512 show_mess("Removed", setting->value);
513 return 1;
515 static int remove_theme(void)
517 static char line[MAX_PATH];
518 int fd;
519 int i, num_removed = 0;
520 char *name, *value;
521 bool needs_to_check_whether_used = false;
523 /* initialize for safe */
524 for (i=0; i<NUM_REMOVE_ITEMS; i++)
525 remove_list[i].value[0] = 0;
527 /* load settings */
528 fd = rb->open(themefile, O_RDONLY);
529 if (fd < 0) return fd;
530 while (rb->read_line(fd, line, sizeof(line)) > 0)
532 if (!rb->settings_parseline(line, &name, &value))
533 continue;
534 /* remove trailing spaces. */
535 char *p = value+rb->strlen(value)-1;
536 while (*p == ' ') *p-- = 0;
537 if (*value == 0 || !rb->strcmp(value, "-"))
538 continue;
539 for (i=0; i<NUM_REMOVE_ITEMS; i++)
541 struct remove_setting *setting = &remove_list[i];
542 if (!rb->strcmp(name, setting->name))
544 set_file_name(setting->value, value, setting);
545 if(setting->option == REMOVE_IF_NOT_USED)
546 needs_to_check_whether_used = true;
547 break;
551 rb->close(fd);
553 if(needs_to_check_whether_used)
554 check_whether_used();
556 /* now remove file assosiated to the theme. */
557 for (i=0; i<NUM_REMOVE_ITEMS; i++)
559 if (remove_list[i].value[0])
561 int ret = remove_file(&remove_list[i]);
562 if (ret < 0)
563 return ret;
564 num_removed += ret;
568 /* remove the setting file iff it is in theme directory to protect
569 * aginst accidental removal of non theme cfg file. if the file is
570 * not in the theme directory, the file may not be a theme cfg file. */
571 if (rb->strncasecmp(themefile, THEME_DIR "/", sizeof(THEME_DIR "/")-1))
573 show_mess("Skipped", themefile);
575 else if (rb->remove(themefile))
577 show_mess("Failed", themefile);
578 return -1;
580 else
582 show_mess("Removed", themefile);
583 rb->reload_directory();
584 num_removed++;
586 return num_removed;
589 static bool option_changed = false;
590 static bool option_menu(void)
592 MENUITEM_STRINGLIST(option_menu, "Remove Options", NULL,
593 /* same order as remove_list */
594 #ifdef HAVE_LCD_BITMAP
595 "Font",
596 #endif
597 "WPS",
598 #ifdef HAVE_LCD_BITMAP
599 "Statusbar Skin",
600 #endif
601 #ifdef HAVE_REMOTE_LCD
602 "Remote WPS",
603 "Remote Statusbar Skin",
604 #endif
605 #if LCD_DEPTH > 1
606 "Backdrop",
607 #endif
608 #ifdef HAVE_LCD_BITMAP
609 "Iconset", "Viewers Iconset",
610 #endif
611 #ifdef HAVE_REMOTE_LCD
612 "Remote Iconset", "Remote Viewers Iconset",
613 #endif
614 #ifdef HAVE_LCD_COLOR
615 "Filetype Colours",
616 #endif
617 "Create Log File");
618 struct opt_items remove_names[] = {
619 {"Always Remove", -1}, {"Never Remove", -1},
620 {"Remove if not Used", -1}, {"Ask for Removal", -1},
622 int selected = 0, result;
624 while (1)
626 result = rb->do_menu(&option_menu, &selected, NULL, false);
627 if (result >= 0 && result < NUM_REMOVE_ITEMS)
629 struct remove_setting *setting = &remove_list[result];
630 int prev_option = setting->option;
631 if (rb->set_option(option_menu_[result], &setting->option, INT,
632 remove_names, NUM_REMOVE_OPTION, NULL))
633 return true;
634 if (prev_option != setting->option)
635 option_changed = true;
637 else if (result == NUM_REMOVE_ITEMS)
639 bool prev_value = create_log;
640 if(rb->set_bool("Create Log File", &create_log))
641 return true;
642 if (prev_value != create_log)
643 option_changed = true;
645 else if (result == MENU_ATTACHED_USB)
646 return true;
647 else
648 return false;
651 return false;
654 enum plugin_status plugin_start(const void* parameter)
656 static char title[64];
657 char *p;
658 MENUITEM_STRINGLIST(menu, title, NULL,
659 "Remove Theme", "Remove Options",
660 "Quit");
661 int selected = 0, ret;
662 bool exit = false;
664 if (!parameter)
665 return PLUGIN_ERROR;
667 rb->snprintf(title, sizeof(title), "Remove %s",
668 rb->strrchr(parameter, '/')+1);
669 if((p = rb->strrchr(title, '.')))
670 *p = 0;
672 #ifdef HAVE_LCD_BITMAP
673 rb->snprintf(font_file, MAX_PATH, FONT_DIR "/%s.fnt",
674 rb->global_settings->font_file);
675 #endif
676 rb->strlcpy(themefile, parameter, MAX_PATH);
677 if (!rb->file_exists(themefile))
679 rb->splash(HZ, "File open error!");
680 return PLUGIN_ERROR;
682 configfile_load(CONFIG_FILENAME, config, nb_config, 0);
683 while (!exit)
685 switch (rb->do_menu(&menu, &selected, NULL, false))
687 case 0:
688 if(create_log)
690 log_fd = rb->open(LOG_FILENAME, O_WRONLY|O_CREAT|O_APPEND, 0666);
691 if(log_fd >= 0)
692 rb->fdprintf(log_fd, "---- %s ----\n", title);
693 else
694 show_mess("Couldn't open log file.", NULL);
696 ret = remove_theme();
697 p = (ret >= 0? "Successfully removed!": "Remove failure");
698 show_mess(p, NULL);
699 if(log_fd >= 0)
701 rb->fdprintf(log_fd, "----------------\n");
702 rb->close(log_fd);
703 log_fd = -1;
705 rb->lcd_clear_display();
706 rb->lcd_update();
707 rb->splashf(0, "%s %s", p, "Press any key to exit.");
708 rb->button_clear_queue();
709 rb->button_get(true);
710 exit = true;
711 break;
712 case 1:
713 if (option_menu())
714 return PLUGIN_USB_CONNECTED;
715 break;
716 case 2:
717 exit = true;
718 break;
719 case MENU_ATTACHED_USB:
720 return PLUGIN_USB_CONNECTED;
721 break;
722 default:
723 break;
726 if(option_changed)
727 configfile_save(CONFIG_FILENAME, config, nb_config, 0);
728 return PLUGIN_OK;