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