summaryrefslogtreecommitdiff
path: root/apps/plugins/theme_remove.c
diff options
context:
space:
mode:
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "plugin.h"
21#include "lib/configfile.h"
22
23PLUGIN_HEADER
24
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"
28
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"
32
33enum remove_option {
34 ALWAYS_REMOVE,
35 NEVER_REMOVE,
36 REMOVE_IF_NOT_USED,
37 ASK_FOR_REMOVAL,
38 NUM_REMOVE_OPTION
39};
40
41struct 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;
48};
49
50static int remove_wps(struct remove_setting *);
51#ifdef HAVE_LCD_BITMAP
52static int remove_icons(struct remove_setting *setting);
53#endif
54
55enum remove_settings {
56#ifdef HAVE_LCD_BITMAP
57 REMOVE_FONT,
58#endif
59 REMOVE_WPS,
60#ifdef HAVE_REMOTE_LCD
61 REMOVE_RWPS,
62#endif
63#if LCD_DEPTH > 1
64 REMOVE_BACKDROP,
65#endif
66#ifdef HAVE_LCD_BITMAP
67 REMOVE_ICON,
68 REMOVE_VICON,
69#endif
70#ifdef HAVE_REMOTE_LCD
71 REMOVE_RICON,
72 REMOVE_RVICON,
73#endif
74#ifdef HAVE_LCD_COLOR
75 REMOVE_COLOURS,
76#endif
77 NUM_REMOVE_ITEMS
78};
79
80static bool create_log = true;
81static struct remove_setting remove_list[NUM_REMOVE_ITEMS] = {
82#ifdef HAVE_LCD_BITMAP
83 [REMOVE_FONT] = { "font", FONT_DIR "/", ".fnt", "",
84 ASK_FOR_REMOVAL, NULL, false },
85#endif
86 [REMOVE_WPS] = { "wps", WPS_DIR "/", ".wps", "",
87 REMOVE_IF_NOT_USED, remove_wps, false },
88#ifdef HAVE_REMOTE_LCD
89 [REMOVE_RWPS] = { "rwps", WPS_DIR "/", ".rwps", "",
90 REMOVE_IF_NOT_USED, remove_wps, false },
91#endif
92#if LCD_DEPTH > 1
93 [REMOVE_BACKDROP] = { "backdrop", BACKDROP_DIR "/", ".bmp", "",
94 REMOVE_IF_NOT_USED, NULL, false },
95#endif
96#ifdef HAVE_LCD_BITMAP
97 [REMOVE_ICON] = { "iconset", ICON_DIR "/", ".bmp", "",
98 ASK_FOR_REMOVAL, NULL, false },
99 [REMOVE_VICON] = { "viewers iconset", ICON_DIR "/", ".bmp", "",
100 ASK_FOR_REMOVAL, remove_icons, false },
101#endif
102#ifdef HAVE_REMOTE_LCD
103 [REMOVE_RICON] = { "remote iconset", ICON_DIR "/", ".bmp", "",
104 ASK_FOR_REMOVAL, NULL, false },
105 [REMOVE_RVICON] = { "remote viewers iconset", ICON_DIR "/", ".bmp", "",
106 ASK_FOR_REMOVAL, NULL, false },
107#endif
108#ifdef HAVE_LCD_COLOR
109 [REMOVE_COLOURS] = { "filetype colours", THEME_DIR "/", ".colours", "",
110 ASK_FOR_REMOVAL, NULL, false },
111#endif
112};
113static char *option_names[NUM_REMOVE_OPTION] = {
114 "always", "never", "not used", "ask",
115};
116static struct configdata config[] = {
117#ifdef HAVE_LCD_BITMAP
118 { TYPE_INT, 0, NUM_REMOVE_OPTION,
119 { .int_p = &remove_list[REMOVE_FONT].option },
120 "remove font", option_names },
121#endif
122 { TYPE_INT, 0, NUM_REMOVE_OPTION,
123 { .int_p = &remove_list[REMOVE_WPS].option },
124 "remove wps", option_names },
125#ifdef HAVE_REMOTE_LCD
126 { TYPE_INT, 0, NUM_REMOVE_OPTION,
127 { .int_p = &remove_list[REMOVE_RWPS].option },
128 "remove rwps", option_names },
129#endif
130#if LCD_DEPTH > 1
131 { TYPE_INT, 0, NUM_REMOVE_OPTION,
132 { .int_p = &remove_list[REMOVE_BACKDROP].option },
133 "remove backdrop", option_names },
134#endif
135#ifdef HAVE_LCD_BITMAP
136 { TYPE_INT, 0, NUM_REMOVE_OPTION,
137 { .int_p = &remove_list[REMOVE_ICON].option },
138 "remove iconset", option_names },
139 { TYPE_INT, 0, NUM_REMOVE_OPTION,
140 { .int_p = &remove_list[REMOVE_VICON].option },
141 "remove viconset", option_names },
142#endif
143#ifdef HAVE_REMOTE_LCD
144 { TYPE_INT, 0, NUM_REMOVE_OPTION,
145 { .int_p = &remove_list[REMOVE_RICON].option },
146 "remove riconset", option_names },
147 { TYPE_INT, 0, NUM_REMOVE_OPTION,
148 { .int_p = &remove_list[REMOVE_RVICON].option },
149 "remove rviconset", option_names },
150#endif
151#ifdef HAVE_LCD_COLOR
152 { TYPE_INT, 0, NUM_REMOVE_OPTION,
153 { .int_p = &remove_list[REMOVE_COLOURS].option },
154 "remove colours", option_names },
155#endif
156 {TYPE_BOOL, 0, 1, { .bool_p = &create_log },
157 "create log", NULL},
158};
159static const int nb_config = sizeof(config)/sizeof(*config);
160static char themefile[MAX_PATH];
161static int log_fd = -1;
162
163static int show_mess(const char *text, const char *file)
164{
165 static char buf[MAX_PATH*2];
166
167 if (file)
168 rb->snprintf(buf, sizeof(buf), "%s: %s", text, file);
169 else
170 rb->snprintf(buf, sizeof(buf), "%s", text);
171
172 DEBUGF("%s\n", buf);
173 if (log_fd >= 0)
174 rb->fdprintf(log_fd, "%s\n", buf);
175
176 rb->splash(0, buf);
177 rb->sleep(HZ/4);
178
179 return 0;
180}
181
182/* set full path of file. */
183static void set_file_name(char *buf, const char*file,
184 struct remove_setting *setting)
185{
186 int len1, len2;
187 if (rb->strncasecmp(file, setting->prefix, rb->strlen(setting->prefix)))
188 rb->snprintf(buf, MAX_PATH, "%s%s", setting->prefix, file);
189 else
190 rb->strlcpy(buf, file, MAX_PATH);
191 len1 = rb->strlen(buf);
192 len2 = rb->strlen(setting->suffix);
193 if (rb->strcasecmp(buf+len1-len2, setting->suffix))
194 rb->strlcpy(&buf[len1], setting->suffix, MAX_PATH-len1);
195}
196
197/* taken from apps/onplay.c */
198/* helper function to remove a non-empty directory */
199static int remove_dir(char* dirname, int len)
200{
201 int result = 0;
202 DIR* dir;
203 int dirlen = rb->strlen(dirname);
204
205 dir = rb->opendir(dirname);
206 if (!dir)
207 return -1; /* open error */
208
209 while (true)
210 {
211 struct dirent* entry;
212 /* walk through the directory content */
213 entry = rb->readdir(dir);
214 if (!entry)
215 break;
216
217 dirname[dirlen] ='\0';
218
219 /* append name to current directory */
220 rb->snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name);
221 if (entry->attribute & ATTR_DIRECTORY)
222 {
223 /* remove a subdirectory */
224 if (!rb->strcmp((char *)entry->d_name, ".") ||
225 !rb->strcmp((char *)entry->d_name, ".."))
226 continue; /* skip these */
227
228 result = remove_dir(dirname, len); /* recursion */
229 if (result)
230 break;
231 }
232 else
233 {
234 /* remove a file */
235 result = rb->remove(dirname);
236 }
237 if (ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK))
238 {
239 show_mess("Canceled", NULL);
240 result = -1;
241 break;
242 }
243 }
244 rb->closedir(dir);
245
246 if (!result)
247 { /* remove the now empty directory */
248 dirname[dirlen] = '\0'; /* terminate to original length */
249
250 result = rb->rmdir(dirname);
251 show_mess("Removed", dirname);
252 }
253
254 return result;
255}
256
257static int remove_wps(struct remove_setting *setting)
258{
259 char bmpdir[MAX_PATH];
260 char *p;
261 rb->strcpy(bmpdir, setting->value);
262 p = rb->strrchr(bmpdir, '.');
263 if (p) *p = 0;
264 if (!rb->dir_exists(bmpdir))
265 return 0;
266 return remove_dir(bmpdir, MAX_PATH);
267}
268
269#ifdef HAVE_LCD_BITMAP
270static int remove_icons(struct remove_setting *setting)
271{
272 char path[MAX_PATH];
273 char *p;
274 rb->strcpy(path, setting->value);
275 p = rb->strrchr(path, '.');
276 rb->strlcpy(p, ".icons", path+MAX_PATH-p);
277
278 if (!rb->file_exists(path))
279 {
280 return 0;
281 }
282 if (rb->remove(path))
283 {
284 show_mess("Failed", path);
285 return 1;
286 }
287 show_mess("Removed", path);
288 return 0;
289}
290#endif
291
292#ifdef HAVE_LCD_BITMAP
293static char font_file[MAX_PATH];
294#endif
295static bool is_deny_file(const char *file)
296{
297 const char *deny_files[] = {
298 WPS_DEFAULTCFG,
299 RWPS_DEFAULTCFG,
300#ifdef HAVE_LCD_BITMAP
301 font_file,
302#endif
303 NULL
304 };
305 const char **p = deny_files;
306 while ( *p )
307 {
308 if (!rb->strcmp(file, *p))
309 return true;
310 p++;
311 }
312 return false;
313}
314
315static void check_whether_used_in_setting(void)
316{
317 const char *setting_files[] = {
318#ifdef HAVE_LCD_BITMAP
319 rb->global_settings->font_file,
320#endif
321 rb->global_settings->wps_file,
322#ifdef HAVE_REMOTE_LCD
323 rb->global_settings->rwps_file,
324#endif
325#if LCD_DEPTH > 1
326 rb->global_settings->backdrop_file,
327#endif
328#ifdef HAVE_LCD_BITMAP
329 rb->global_settings->icon_file,
330 rb->global_settings->viewers_icon_file,
331#endif
332#ifdef HAVE_REMOTE_LCD
333 rb->global_settings->remote_icon_file,
334 rb->global_settings->remote_viewers_icon_file,
335#endif
336#ifdef HAVE_LCD_COLOR
337 rb->global_settings->colors_file,
338#endif
339 };
340 char tempfile[MAX_PATH];
341 int i;
342 for (i=0; i<NUM_REMOVE_ITEMS; i++)
343 {
344 struct remove_setting *setting = &remove_list[i];
345 if (setting->value[0])
346 {
347 set_file_name(tempfile, setting_files[i], setting);
348 if (!rb->strcasecmp(tempfile, setting->value))
349 setting->used = true;
350 }
351 }
352}
353static void check_whether_used_in_file(const char *cfgfile)
354{
355 char line[MAX_PATH];
356 char settingfile[MAX_PATH];
357 char *p;
358 int fd;
359 char *name, *value;
360 int i;
361
362 if (!rb->strcasecmp(themefile, cfgfile))
363 return;
364 fd = rb->open(cfgfile, O_RDONLY);
365 if (fd < 0)
366 return;
367 while (rb->read_line(fd, line, sizeof(line)) > 0)
368 {
369 if (!rb->settings_parseline(line, &name, &value))
370 continue;
371 /* remove trailing spaces. */
372 p = value+rb->strlen(value)-1;
373 while (*p == ' ') *p-- = 0;
374 if (*value == 0 || !rb->strcmp(value, "-"))
375 continue;
376 for (i=0; i<NUM_REMOVE_ITEMS; i++)
377 {
378 struct remove_setting *setting = &remove_list[i];
379 if (!rb->strcmp(name, setting->name))
380 {
381 if (setting->value[0])
382 {
383 set_file_name(settingfile, value, setting);
384 if (!rb->strcasecmp(settingfile, setting->value))
385 setting->used = true;
386 }
387 break;
388 }
389 }
390 }
391 rb->close(fd);
392}
393static void check_whether_used(void)
394{
395 char cfgfile[MAX_PATH];
396 DIR *dir;
397
398 check_whether_used_in_setting();
399#ifdef HAVE_LCD_BITMAP
400 /* mark font files come from rockbox-font.zip as used and don't remove
401 * them automatically as themes may depend on those fonts. */
402 if (remove_list[REMOVE_FONT].option == REMOVE_IF_NOT_USED)
403 check_whether_used_in_file(RB_FONTS_CONFIG);
404#endif
405
406 dir = rb->opendir(THEME_DIR);
407 if (!dir)
408 return; /* open error */
409
410 while (true)
411 {
412 struct dirent* entry;
413 char *p;
414 int i;
415 /* walk through the directory content */
416 entry = rb->readdir(dir);
417 if (!entry)
418 break;
419 p = rb->strrchr(entry->d_name, '.');
420 if (!p || rb->strcmp(p, ".cfg"))
421 continue;
422
423 rb->snprintf(cfgfile, MAX_PATH, "%s/%s", THEME_DIR, entry->d_name);
424 check_whether_used_in_file(cfgfile);
425 /* break the loop if all files need to be checked in the theme
426 * turned out to be used. */
427 for (i = 0; i < NUM_REMOVE_ITEMS; i++)
428 {
429 struct remove_setting *setting = &remove_list[i];
430 if (setting->option == REMOVE_IF_NOT_USED)
431 {
432 if (setting->value[0] && !setting->used)
433 break;
434 }
435 }
436 if (i == NUM_REMOVE_ITEMS)
437 break;
438 }
439 rb->closedir(dir);
440}
441
442static int remove_file(struct remove_setting *setting)
443{
444 if (!rb->file_exists(setting->value))
445 {
446 show_mess("Doesn't exist", setting->value);
447 return 0;
448 }
449 if (is_deny_file(setting->value))
450 {
451 show_mess("Denied", setting->value);
452 return 0;
453 }
454 switch (setting->option)
455 {
456 case ALWAYS_REMOVE:
457 break;
458 case NEVER_REMOVE:
459 show_mess("Skipped", setting->value);
460 return 0;
461 break;
462 case REMOVE_IF_NOT_USED:
463 if (setting->used)
464 {
465 show_mess("Used", setting->value);
466 return 0;
467 }
468 break;
469 case ASK_FOR_REMOVAL:
470 default:
471 {
472 const char *message_lines[] = { "Delete?", setting->value };
473 const struct text_message text_message = { message_lines, 2 };
474 if (rb->gui_syncyesno_run(&text_message, NULL, NULL) != YESNO_YES)
475 {
476 show_mess("Skipped", setting->value);
477 return 0;
478 }
479 }
480 break;
481 }
482 if (rb->remove(setting->value))
483 {
484 show_mess("Failed", setting->value);
485 return -1;
486 }
487 if (setting->func && setting->func(setting))
488 return -1;
489 show_mess("Removed", setting->value);
490 return 1;
491}
492static int remove_theme(void)
493{
494 static char line[MAX_PATH];
495 int fd;
496 int i, num_removed = 0;
497 char *name, *value;
498 bool needs_to_check_whether_used = false;
499
500 /* initialize for safe */
501 for (i=0; i<NUM_REMOVE_ITEMS; i++)
502 remove_list[i].value[0] = 0;
503
504 /* load settings */
505 fd = rb->open(themefile, O_RDONLY);
506 if (fd < 0) return fd;
507 while (rb->read_line(fd, line, sizeof(line)) > 0)
508 {
509 if (!rb->settings_parseline(line, &name, &value))
510 continue;
511 /* remove trailing spaces. */
512 char *p = value+rb->strlen(value)-1;
513 while (*p == ' ') *p-- = 0;
514 if (*value == 0 || !rb->strcmp(value, "-"))
515 continue;
516 for (i=0; i<NUM_REMOVE_ITEMS; i++)
517 {
518 struct remove_setting *setting = &remove_list[i];
519 if (!rb->strcmp(name, setting->name))
520 {
521 set_file_name(setting->value, value, setting);
522 if(setting->option == REMOVE_IF_NOT_USED)
523 needs_to_check_whether_used = true;
524 break;
525 }
526 }
527 }
528 rb->close(fd);
529
530 if(needs_to_check_whether_used)
531 check_whether_used();
532
533 /* now remove file assosiated to the theme. */
534 for (i=0; i<NUM_REMOVE_ITEMS; i++)
535 {
536 if (remove_list[i].value[0])
537 {
538 int ret = remove_file(&remove_list[i]);
539 if (ret < 0)
540 return ret;
541 num_removed += ret;
542 }
543 }
544
545 /* remove the setting file iff it is in theme directory to protect
546 * aginst accidental removal of non theme cfg file. if the file is
547 * not in the theme directory, the file may not be a theme cfg file. */
548 if (rb->strncasecmp(themefile, THEME_DIR "/", sizeof(THEME_DIR "/")-1))
549 {
550 show_mess("Skipped", themefile);
551 }
552 else if (rb->remove(themefile))
553 {
554 show_mess("Failed", themefile);
555 return -1;
556 }
557 else
558 {
559 show_mess("Removed", themefile);
560 rb->reload_directory();
561 num_removed++;
562 }
563 return num_removed;
564}
565
566static bool option_changed = false;
567static bool option_menu(void)
568{
569 MENUITEM_STRINGLIST(option_menu, "Remove Options", NULL,
570 /* same order as remove_list */
571#ifdef HAVE_LCD_BITMAP
572 "Font",
573#endif
574 "WPS",
575#ifdef HAVE_REMOTE_LCD
576 "Remote WPS",
577#endif
578#if LCD_DEPTH > 1
579 "Backdrop",
580#endif
581#ifdef HAVE_LCD_BITMAP
582 "Iconset", "Viewers Iconset",
583#endif
584#ifdef HAVE_REMOTE_LCD
585 "Remote Iconset", "Remote Viewers Iconset",
586#endif
587#ifdef HAVE_LCD_COLOR
588 "Filetype Colours",
589#endif
590 "Create Log File");
591 struct opt_items remove_names[] = {
592 {"Always Remove", -1}, {"Never Remove", -1},
593 {"Remove if not Used", -1}, {"Ask for Removal", -1},
594 };
595 int selected = 0, result;
596
597 while (1)
598 {
599 result = rb->do_menu(&option_menu, &selected, NULL, false);
600 if (result >= 0 && result < NUM_REMOVE_ITEMS)
601 {
602 struct remove_setting *setting = &remove_list[result];
603 int prev_option = setting->option;
604 if (rb->set_option(option_menu_[result], &setting->option, INT,
605 remove_names, NUM_REMOVE_OPTION, NULL))
606 return true;
607 if (prev_option != setting->option)
608 option_changed = true;
609 }
610 else if (result == NUM_REMOVE_ITEMS)
611 {
612 bool prev_value = create_log;
613 if(rb->set_bool("Create Log File", &create_log))
614 return true;
615 if (prev_value != create_log)
616 option_changed = true;
617 }
618 else if (result == MENU_ATTACHED_USB)
619 return true;
620 else
621 return false;
622 }
623
624 return false;
625}
626
627enum plugin_status plugin_start(const void* parameter)
628{
629 static char title[64];
630 char *p;
631 MENUITEM_STRINGLIST(menu, title, NULL,
632 "Remove Theme", "Remove Options",
633 "Quit");
634 int selected = 0, ret;
635 bool exit = false;
636
637 if (!parameter)
638 return PLUGIN_ERROR;
639
640 rb->snprintf(title, sizeof(title), "Remove %s",
641 rb->strrchr(parameter, '/')+1);
642 if((p = rb->strrchr(title, '.')))
643 *p = 0;
644
645#ifdef HAVE_LCD_BITMAP
646 rb->snprintf(font_file, MAX_PATH, FONT_DIR "/%s.fnt",
647 rb->global_settings->font_file);
648#endif
649 rb->strlcpy(themefile, parameter, MAX_PATH);
650 if (!rb->file_exists(themefile))
651 {
652 rb->splash(HZ, "File open error!");
653 return PLUGIN_ERROR;
654 }
655 configfile_load(CONFIG_FILENAME, config, nb_config, 0);
656 while (!exit)
657 {
658 switch (rb->do_menu(&menu, &selected, NULL, false))
659 {
660 case 0:
661 if(create_log)
662 {
663 log_fd = rb->open(LOG_FILENAME, O_WRONLY|O_CREAT|O_APPEND);
664 if(log_fd >= 0)
665 rb->fdprintf(log_fd, "---- %s ----\n", title);
666 else
667 show_mess("Couldn't open log file.", NULL);
668 }
669 ret = remove_theme();
670 p = (ret >= 0? "Successfully removed!": "Remove failure");
671 show_mess(p, NULL);
672 if(log_fd >= 0)
673 {
674 rb->fdprintf(log_fd, "----------------\n");
675 rb->close(log_fd);
676 log_fd = -1;
677 }
678 rb->lcd_clear_display();
679 rb->lcd_update();
680 rb->splashf(0, "%s %s", p, "Press any key to exit.");
681 rb->button_clear_queue();
682 rb->button_get(true);
683 exit = true;
684 break;
685 case 1:
686 if (option_menu())
687 return PLUGIN_USB_CONNECTED;
688 break;
689 case 2:
690 exit = true;
691 break;
692 case MENU_ATTACHED_USB:
693 return PLUGIN_USB_CONNECTED;
694 break;
695 default:
696 break;
697 }
698 }
699 if(option_changed)
700 configfile_save(CONFIG_FILENAME, config, nb_config, 0);
701 return PLUGIN_OK;
702}