summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/bookmark.c8
-rw-r--r--apps/codecs.c2
-rw-r--r--apps/debug_menu.c86
-rw-r--r--apps/filetree.c4
-rw-r--r--apps/main.c102
-rw-r--r--apps/menus/display_menu.c20
-rw-r--r--apps/menus/main_menu.c18
-rw-r--r--apps/menus/settings_menu.c6
-rw-r--r--apps/misc.c16
-rw-r--r--apps/onplay.c844
-rwxr-xr-xapps/playlist.c237
-rw-r--r--apps/playlist_catalog.c2
-rw-r--r--apps/plugin.c264
-rw-r--r--apps/plugin.h45
-rw-r--r--apps/plugins/properties.c14
-rw-r--r--apps/radio/presets.c2
-rw-r--r--apps/radio/radioart.c2
-rw-r--r--apps/recorder/albumart.c2
-rw-r--r--apps/recorder/recording.c2
-rw-r--r--apps/root_menu.c5
-rw-r--r--apps/scrobbler.c2
-rw-r--r--apps/settings.c8
-rw-r--r--apps/settings_list.c6
-rw-r--r--apps/shortcuts.c2
-rw-r--r--apps/tagcache.c21
-rw-r--r--apps/tree.c55
26 files changed, 964 insertions, 811 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c
index 3234e77d9b..567f98ac29 100644
--- a/apps/bookmark.c
+++ b/apps/bookmark.c
@@ -41,7 +41,7 @@
41#include "list.h" 41#include "list.h"
42#include "plugin.h" 42#include "plugin.h"
43#include "file.h" 43#include "file.h"
44#include "filefuncs.h" 44#include "pathfuncs.h"
45 45
46#define MAX_BOOKMARKS 10 46#define MAX_BOOKMARKS 10
47#define MAX_BOOKMARK_SIZE 350 47#define MAX_BOOKMARK_SIZE 350
@@ -1090,10 +1090,10 @@ static bool generate_bookmark_file_name(const char *in)
1090 { 1090 {
1091#ifdef HAVE_MULTIVOLUME 1091#ifdef HAVE_MULTIVOLUME
1092 /* The "root" of an extra volume need special handling too. */ 1092 /* The "root" of an extra volume need special handling too. */
1093 bool volume_root = (strip_volume(in, global_bookmark_file_name) && 1093 const char *filename;
1094 !strcmp("/", global_bookmark_file_name)); 1094 path_strip_volume(in, &filename, true);
1095 bool volume_root = *filename == '\0';
1095#endif 1096#endif
1096
1097 strcpy(global_bookmark_file_name, in); 1097 strcpy(global_bookmark_file_name, in);
1098 if(global_bookmark_file_name[len-1] == '/') 1098 if(global_bookmark_file_name[len-1] == '/')
1099 len--; 1099 len--;
diff --git a/apps/codecs.c b/apps/codecs.c
index e84cdf88aa..cabc9ba993 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -128,7 +128,7 @@ struct codec_api ci = {
128 logf, 128 logf,
129#endif 129#endif
130 130
131 (qsort_func)qsort, 131 (void *)qsort,
132 132
133#ifdef RB_PROFILE 133#ifdef RB_PROFILE
134 profile_thread, 134 profile_thread,
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 61698f5025..75e23b3945 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -528,14 +528,19 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
528{ 528{
529 (void)data; 529 (void)data;
530 int partition = selected_item/2; 530 int partition = selected_item/2;
531 struct partinfo* p = disk_partinfo(partition); 531
532 struct partinfo p;
533 if (!disk_partinfo(partition, &p))
534 return buffer;
535
532 if (selected_item%2) 536 if (selected_item%2)
533 { 537 {
534 snprintf(buffer, buffer_len, " T:%x %ld MB", p->type, p->size / ( 2048 / ( SECTOR_SIZE / 512 ))); 538 snprintf(buffer, buffer_len, " T:%x %ld MB", p.type,
539 p.size / ( 2048 / ( SECTOR_SIZE / 512 )));
535 } 540 }
536 else 541 else
537 { 542 {
538 snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p->start); 543 snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p.start);
539 } 544 }
540 return buffer; 545 return buffer;
541} 546}
@@ -1377,7 +1382,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1377 simplelist_addline( 1382 simplelist_addline(
1378 "Size: %s", buf); 1383 "Size: %s", buf);
1379 unsigned long free; 1384 unsigned long free;
1380 fat_size( IF_MV(0,) NULL, &free ); 1385 volume_size( IF_MV(0,) NULL, &free );
1381 simplelist_addline( 1386 simplelist_addline(
1382 "Free: %ld MB", free / 1024); 1387 "Free: %ld MB", free / 1024);
1383 simplelist_addline( 1388 simplelist_addline(
@@ -1469,7 +1474,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1469 "No timing info"); 1474 "No timing info");
1470 } 1475 }
1471 simplelist_addline( 1476 simplelist_addline(
1472 "Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0))); 1477 "Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0)));
1473#ifdef HAVE_ATA_DMA 1478#ifdef HAVE_ATA_DMA
1474 i = ata_get_dma_mode(); 1479 i = ata_get_dma_mode();
1475 if (i == 0) { 1480 if (i == 0) {
@@ -1496,11 +1501,11 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1496 simplelist_addline( 1501 simplelist_addline(
1497 "Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024); 1502 "Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024);
1498 unsigned long free; 1503 unsigned long free;
1499 fat_size( IF_MV(0,) NULL, &free ); 1504 volume_size( IF_MV(0,) NULL, &free );
1500 simplelist_addline( 1505 simplelist_addline(
1501 "Free: %ld MB", free / 1024); 1506 "Free: %ld MB", free / 1024);
1502 simplelist_addline( 1507 simplelist_addline(
1503 "Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0))); 1508 "Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0)));
1504 return btn; 1509 return btn;
1505} 1510}
1506#endif 1511#endif
@@ -1542,29 +1547,64 @@ static bool dbg_disk_info(void)
1542#ifdef HAVE_DIRCACHE 1547#ifdef HAVE_DIRCACHE
1543static int dircache_callback(int btn, struct gui_synclist *lists) 1548static int dircache_callback(int btn, struct gui_synclist *lists)
1544{ 1549{
1545 (void)lists; 1550 struct dircache_info info;
1551 dircache_get_info(&info);
1552
1553 if (global_settings.dircache)
1554 {
1555 switch (btn)
1556 {
1557 case ACTION_STD_CONTEXT:
1558 splash(HZ/2, "Rebuilding cache");
1559 dircache_suspend();
1560 *(int *)lists->data = dircache_resume();
1561 case ACTION_UNKNOWN:
1562 btn = ACTION_NONE;
1563 break;
1564 #ifdef DIRCACHE_DUMPSTER
1565 case ACTION_STD_OK:
1566 splash(0, "Dumping cache");
1567 dircache_dump();
1568 btn = ACTION_NONE;
1569 break;
1570 #endif /* DIRCACHE_DUMPSTER */
1571 case ACTION_STD_CANCEL:
1572 if (*(int *)lists->data > 0 && info.status == DIRCACHE_SCANNING)
1573 {
1574 splash(HZ, str(LANG_SCANNING_DISK));
1575 btn = ACTION_NONE;
1576 }
1577 break;
1578 }
1579 }
1580
1546 simplelist_set_line_count(0); 1581 simplelist_set_line_count(0);
1547 simplelist_addline("Cache initialized: %s", 1582
1548 dircache_is_enabled() ? "Yes" : "No"); 1583 simplelist_addline("Cache status: %s", info.statusdesc);
1549 simplelist_addline("Cache size: %d B", 1584 simplelist_addline("Last size: %lu B", info.last_size);
1550 dircache_get_cache_size()); 1585 simplelist_addline("Size: %lu B", info.size);
1551 simplelist_addline("Last size: %d B", 1586 unsigned int utilized = info.size ? 1000ull*info.sizeused / info.size : 0;
1552 global_status.dircache_size); 1587 simplelist_addline("Used: %lu B (%u.%u%%)", info.sizeused,
1553 simplelist_addline("Limit: %d B", 1588 utilized / 10, utilized % 10);
1554 DIRCACHE_LIMIT); 1589 simplelist_addline("Limit: %lu B", info.size_limit);
1555 simplelist_addline("Reserve: %d/%d B", 1590 simplelist_addline("Reserve: %lu/%lu B", info.reserve_used, info.reserve);
1556 dircache_get_reserve_used(), DIRCACHE_RESERVE); 1591 long ticks = ALIGN_UP(info.build_ticks, HZ / 10);
1557 simplelist_addline("Scanning took: %d s", 1592 simplelist_addline("Scanning took: %ld.%ld s",
1558 dircache_get_build_ticks() / HZ); 1593 ticks / HZ, (ticks*10 / HZ) % 10);
1559 simplelist_addline("Entry count: %d", 1594 simplelist_addline("Entry count: %u", info.entry_count);
1560 dircache_get_entry_count()); 1595
1596 if (btn == ACTION_NONE)
1597 btn = ACTION_REDRAW;
1598
1561 return btn; 1599 return btn;
1600 (void)lists;
1562} 1601}
1563 1602
1564static bool dbg_dircache_info(void) 1603static bool dbg_dircache_info(void)
1565{ 1604{
1566 struct simplelist_info info; 1605 struct simplelist_info info;
1567 simplelist_info_init(&info, "Dircache Info", 7, NULL); 1606 int syncbuild = 0;
1607 simplelist_info_init(&info, "Dircache Info", 8, &syncbuild);
1568 info.action_callback = dircache_callback; 1608 info.action_callback = dircache_callback;
1569 info.hide_selection = true; 1609 info.hide_selection = true;
1570 info.scroll_all = true; 1610 info.scroll_all = true;
diff --git a/apps/filetree.c b/apps/filetree.c
index 319b5f4a77..64283b274b 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -376,9 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir)
376 ++files_in_dir; 376 ++files_in_dir;
377 377
378 dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; 378 dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used;
379 dptr->time_write = 379 dptr->time_write = info.mtime;
380 (long)info.wrtdate<<16 |
381 (long)info.wrttime; /* in one # */
382 strcpy(dptr->name, (char *)entry->d_name); 380 strcpy(dptr->name, (char *)entry->d_name);
383 name_buffer_used += len + 1; 381 name_buffer_used += len + 1;
384 382
diff --git a/apps/main.c b/apps/main.c
index 6c6f0d6aba..9098180fb8 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -19,11 +19,12 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "config.h" 21#include "config.h"
22#include "system.h"
22 23
23#include "gcc_extensions.h" 24#include "gcc_extensions.h"
24#include "storage.h" 25#include "storage.h"
25#include "disk.h" 26#include "disk.h"
26#include "fat.h" 27#include "file_internal.h"
27#include "lcd.h" 28#include "lcd.h"
28#include "rtc.h" 29#include "rtc.h"
29#include "debug.h" 30#include "debug.h"
@@ -34,7 +35,6 @@
34#include "filetypes.h" 35#include "filetypes.h"
35#include "panic.h" 36#include "panic.h"
36#include "menu.h" 37#include "menu.h"
37#include "system.h"
38#include "usb.h" 38#include "usb.h"
39#include "powermgmt.h" 39#include "powermgmt.h"
40#include "adc.h" 40#include "adc.h"
@@ -203,80 +203,53 @@ int main(void)
203 root_menu(); 203 root_menu();
204} 204}
205 205
206static int init_dircache(bool preinit) INIT_ATTR;
207static int init_dircache(bool preinit)
208{
209#ifdef HAVE_DIRCACHE 206#ifdef HAVE_DIRCACHE
210 int result = 0; 207static int INIT_ATTR init_dircache(bool preinit)
211 bool clear = false; 208{
212
213 if (preinit) 209 if (preinit)
214 dircache_init(); 210 dircache_init(MAX(global_status.dircache_size, 0));
215 211
216 if (!global_settings.dircache) 212 if (!global_settings.dircache)
217 return 0; 213 return -1;
214
215 int result = -1;
218 216
219# ifdef HAVE_EEPROM_SETTINGS 217#ifdef HAVE_EEPROM_SETTINGS
220 if (firmware_settings.initialized && firmware_settings.disk_clean 218 if (firmware_settings.initialized &&
221 && preinit) 219 firmware_settings.disk_clean &&
220 preinit)
222 { 221 {
223 result = dircache_load(); 222 result = dircache_load();
224
225 if (result < 0) 223 if (result < 0)
226 {
227 firmware_settings.disk_clean = false; 224 firmware_settings.disk_clean = false;
228 if (global_status.dircache_size <= 0)
229 {
230 /* This will be in default language, settings are not
231 applied yet. Not really any easy way to fix that. */
232 splash(0, str(LANG_SCANNING_DISK));
233 clear = true;
234 }
235
236 dircache_build(global_status.dircache_size);
237 }
238 } 225 }
239 else 226 else
240# endif 227#endif /* HAVE_EEPROM_SETTINGS */
228 if (!preinit)
241 { 229 {
242 if (preinit) 230 result = dircache_enable();
243 return -1; 231 if (result != 0)
244
245 if (!dircache_is_enabled()
246 && !dircache_is_initializing())
247 { 232 {
248 if (global_status.dircache_size <= 0) 233 if (result > 0)
249 { 234 {
235 /* Print "Scanning disk..." to the display. */
250 splash(0, str(LANG_SCANNING_DISK)); 236 splash(0, str(LANG_SCANNING_DISK));
251 clear = true; 237 dircache_wait();
238 backlight_on();
239 show_logo();
252 } 240 }
253 result = dircache_build(global_status.dircache_size);
254 }
255 241
256 if (result < 0) 242 struct dircache_info info;
257 { 243 dircache_get_info(&info);
258 /* Initialization of dircache failed. Manual action is 244 global_status.dircache_size = info.size;
259 * necessary to enable dircache again. 245 status_save();
260 */
261 splashf(0, "Dircache failed, disabled. Result: %d", result);
262 global_settings.dircache = false;
263 } 246 }
264 } 247 /* else don't wait or already enabled by load */
265
266 if (clear)
267 {
268 backlight_on();
269 show_logo();
270 global_status.dircache_size = dircache_get_cache_size();
271 status_save();
272 } 248 }
273 249
274 return result; 250 return result;
275#else
276 (void)preinit;
277 return 0;
278#endif
279} 251}
252#endif /* HAVE_DIRCACHE */
280 253
281#ifdef HAVE_TAGCACHE 254#ifdef HAVE_TAGCACHE
282static void init_tagcache(void) INIT_ATTR; 255static void init_tagcache(void) INIT_ATTR;
@@ -363,6 +336,7 @@ static void init(void)
363 button_init(); 336 button_init();
364 powermgmt_init(); 337 powermgmt_init();
365 backlight_init(); 338 backlight_init();
339 unicode_init();
366#ifdef SIMULATOR 340#ifdef SIMULATOR
367 sim_tasks_init(); 341 sim_tasks_init();
368#endif 342#endif
@@ -392,8 +366,10 @@ static void init(void)
392 settings_reset(); 366 settings_reset();
393 settings_load(SETTINGS_ALL); 367 settings_load(SETTINGS_ALL);
394 settings_apply(true); 368 settings_apply(true);
369#ifdef HAVE_DIRCACHE
395 init_dircache(true); 370 init_dircache(true);
396 init_dircache(false); 371 init_dircache(false);
372#endif
397#ifdef HAVE_TAGCACHE 373#ifdef HAVE_TAGCACHE
398 init_tagcache(); 374 init_tagcache();
399#endif 375#endif
@@ -429,6 +405,8 @@ static void init(void)
429 405
430#else 406#else
431 407
408#include "errno.h"
409
432static void init(void) INIT_ATTR; 410static void init(void) INIT_ATTR;
433static void init(void) 411static void init(void)
434{ 412{
@@ -443,6 +421,9 @@ static void init(void)
443 core_allocator_init(); 421 core_allocator_init();
444 kernel_init(); 422 kernel_init();
445 423
424 /* early early early! */
425 filesystem_init();
426
446#ifdef HAVE_ADJUSTABLE_CPU_FREQ 427#ifdef HAVE_ADJUSTABLE_CPU_FREQ
447 set_cpu_frequency(CPUFREQ_NORMAL); 428 set_cpu_frequency(CPUFREQ_NORMAL);
448#ifdef CPU_COLDFIRE 429#ifdef CPU_COLDFIRE
@@ -462,6 +443,7 @@ static void init(void)
462 /* current_tick should be ticking by now */ 443 /* current_tick should be ticking by now */
463 CHART("ticking"); 444 CHART("ticking");
464 445
446 unicode_init();
465 lcd_init(); 447 lcd_init();
466#ifdef HAVE_REMOTE_LCD 448#ifdef HAVE_REMOTE_LCD
467 lcd_remote_init(); 449 lcd_remote_init();
@@ -558,8 +540,6 @@ static void init(void)
558 } 540 }
559#endif 541#endif
560 542
561
562 disk_init_subsystem();
563 CHART(">storage_init"); 543 CHART(">storage_init");
564 rc = storage_init(); 544 rc = storage_init();
565 CHART("<storage_init"); 545 CHART("<storage_init");
@@ -661,22 +641,24 @@ static void init(void)
661 CHART("<settings_load(ALL)"); 641 CHART("<settings_load(ALL)");
662 } 642 }
663 643
644#ifdef HAVE_DIRCACHE
664 CHART(">init_dircache(true)"); 645 CHART(">init_dircache(true)");
665 rc = init_dircache(true); 646 rc = init_dircache(true);
666 CHART("<init_dircache(true)"); 647 CHART("<init_dircache(true)");
667 if (rc < 0)
668 {
669#ifdef HAVE_TAGCACHE 648#ifdef HAVE_TAGCACHE
649 if (rc < 0)
670 remove(TAGCACHE_STATEFILE); 650 remove(TAGCACHE_STATEFILE);
671#endif 651#endif /* HAVE_TAGCACHE */
672 } 652#endif /* HAVE_DIRCACHE */
673 653
674 CHART(">settings_apply(true)"); 654 CHART(">settings_apply(true)");
675 settings_apply(true); 655 settings_apply(true);
676 CHART("<settings_apply(true)"); 656 CHART("<settings_apply(true)");
657#ifdef HAVE_DIRCACHE
677 CHART(">init_dircache(false)"); 658 CHART(">init_dircache(false)");
678 init_dircache(false); 659 init_dircache(false);
679 CHART("<init_dircache(false)"); 660 CHART("<init_dircache(false)");
661#endif
680#ifdef HAVE_TAGCACHE 662#ifdef HAVE_TAGCACHE
681 CHART(">init_tagcache"); 663 CHART(">init_tagcache");
682 init_tagcache(); 664 init_tagcache();
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
index 3e1443d02e..948dcede00 100644
--- a/apps/menus/display_menu.c
+++ b/apps/menus/display_menu.c
@@ -43,6 +43,7 @@
43#endif 43#endif
44#include "viewport.h" 44#include "viewport.h"
45#include "statusbar.h" /* statusbar_vals enum*/ 45#include "statusbar.h" /* statusbar_vals enum*/
46#include "rbunicode.h"
46 47
47#ifdef HAVE_BACKLIGHT 48#ifdef HAVE_BACKLIGHT
48static int filterfirstkeypress_callback(int action,const struct menu_item_ex *this_item) 49static int filterfirstkeypress_callback(int action,const struct menu_item_ex *this_item)
@@ -524,8 +525,25 @@ MAKE_MENU(touchscreen_menu, ID2P(LANG_TOUCHSCREEN_SETTINGS), NULL, Icon_NOICON,
524 &touchscreen_menu_calibrate, &touchscreen_menu_reset_calibration); 525 &touchscreen_menu_calibrate, &touchscreen_menu_reset_calibration);
525#endif 526#endif
526 527
528static int codepage_callback(int action, const struct menu_item_ex *this_item)
529{
530 static int old_codepage;
531 int new_codepage = global_settings.default_codepage;
532 (void)this_item;
533 switch (action)
534 {
535 case ACTION_ENTER_MENUITEM:
536 old_codepage = new_codepage;
537 break;
538 case ACTION_EXIT_MENUITEM:
539 if (new_codepage != old_codepage)
540 set_codepage(new_codepage);
541 break;
542 }
543 return action;
544}
527 545
528MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, NULL); 546MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, codepage_callback);
529 547
530 548
531MAKE_MENU(display_menu, ID2P(LANG_DISPLAY), 549MAKE_MENU(display_menu, ID2P(LANG_DISPLAY),
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 6a1295996c..8764101f73 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -48,6 +48,7 @@
48#include "time.h" 48#include "time.h"
49#include "wps.h" 49#include "wps.h"
50#include "skin_buffer.h" 50#include "skin_buffer.h"
51#include "disk.h"
51 52
52static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG}; 53static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG};
53 54
@@ -160,14 +161,14 @@ static const char* info_getname(int selected_item, void *data,
160#endif 161#endif
161 if (info->new_data) 162 if (info->new_data)
162 { 163 {
163 fat_size(IF_MV(0,) &info->size, &info->free); 164 volume_size(IF_MV(0,) &info->size, &info->free);
164#ifdef HAVE_MULTIVOLUME 165#ifdef HAVE_MULTIVOLUME
165#ifndef APPLICATION 166#ifndef APPLICATION
166 if (fat_ismounted(1)) 167 volume_size(1, &info->size2, &info->free2);
167 fat_size(1, &info->size2, &info->free2); 168#else
168 else 169 info->size2 = 0;
169#endif 170#endif
170 info->size2 = 0; 171
171#endif 172#endif
172 info->new_data = false; 173 info->new_data = false;
173 } 174 }
@@ -347,12 +348,7 @@ static int info_action_callback(int action, struct gui_synclist *lists)
347 info->new_data = true; 348 info->new_data = true;
348 splash(0, ID2P(LANG_SCANNING_DISK)); 349 splash(0, ID2P(LANG_SCANNING_DISK));
349 for (i = 0; i < NUM_VOLUMES; i++) 350 for (i = 0; i < NUM_VOLUMES; i++)
350 { 351 volume_recalc_free(IF_MV(i));
351#ifdef HAVE_HOTSWAP
352 if (fat_ismounted(i))
353#endif
354 fat_recalc_free(IF_MV(i));
355 }
356#else 352#else
357 (void) lists; 353 (void) lists;
358#endif 354#endif
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index 95423a20fa..0d2a7febf1 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -209,12 +209,12 @@ static int dircache_callback(int action,const struct menu_item_ex *this_item)
209 switch (action) 209 switch (action)
210 { 210 {
211 case ACTION_EXIT_MENUITEM: /* on exit */ 211 case ACTION_EXIT_MENUITEM: /* on exit */
212 if (global_settings.dircache && !dircache_is_enabled()) 212 if (global_settings.dircache)
213 { 213 {
214 if (dircache_build(0) < 0) 214 if (dircache_enable() < 0)
215 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); 215 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
216 } 216 }
217 else if (!global_settings.dircache && dircache_is_enabled()) 217 else
218 { 218 {
219 dircache_disable(); 219 dircache_disable();
220 } 220 }
diff --git a/apps/misc.c b/apps/misc.c
index f847023c31..b6eaafb599 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -28,9 +28,12 @@
28#include "misc.h" 28#include "misc.h"
29#include "system.h" 29#include "system.h"
30#include "lcd.h" 30#include "lcd.h"
31#ifdef HAVE_DIRCACHE
32#include "dircache.h"
33#endif
31#include "file.h" 34#include "file.h"
32#ifndef __PCTOOL__ 35#ifndef __PCTOOL__
33#include "filefuncs.h" 36#include "pathfuncs.h"
34#include "lang.h" 37#include "lang.h"
35#include "dir.h" 38#include "dir.h"
36#ifdef HAVE_REMOTE_LCD 39#ifdef HAVE_REMOTE_LCD
@@ -744,8 +747,7 @@ int show_logo( void )
744*/ 747*/
745void check_bootfile(bool do_rolo) 748void check_bootfile(bool do_rolo)
746{ 749{
747 static unsigned short wrtdate = 0; 750 static time_t mtime = 0;
748 static unsigned short wrttime = 0;
749 DIR* dir = NULL; 751 DIR* dir = NULL;
750 struct dirent* entry = NULL; 752 struct dirent* entry = NULL;
751 753
@@ -761,10 +763,9 @@ void check_bootfile(bool do_rolo)
761 { 763 {
762 struct dirinfo info = dir_get_info(dir, entry); 764 struct dirinfo info = dir_get_info(dir, entry);
763 /* found the bootfile */ 765 /* found the bootfile */
764 if(wrtdate && do_rolo) 766 if(mtime && do_rolo)
765 { 767 {
766 if((info.wrtdate != wrtdate) || 768 if(info.mtime != mtime)
767 (info.wrttime != wrttime))
768 { 769 {
769 static const char *lines[] = { ID2P(LANG_BOOT_CHANGED), 770 static const char *lines[] = { ID2P(LANG_BOOT_CHANGED),
770 ID2P(LANG_REBOOT_NOW) }; 771 ID2P(LANG_REBOOT_NOW) };
@@ -777,8 +778,7 @@ void check_bootfile(bool do_rolo)
777 } 778 }
778 } 779 }
779 } 780 }
780 wrtdate = info.wrtdate; 781 mtime = info.mtime;
781 wrttime = info.wrttime;
782 } 782 }
783 } 783 }
784 closedir(dir); 784 closedir(dir);
diff --git a/apps/onplay.c b/apps/onplay.c
index 7c5f517090..091680e949 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -62,16 +62,13 @@
62#include "statusbar-skinned.h" 62#include "statusbar-skinned.h"
63#include "pitchscreen.h" 63#include "pitchscreen.h"
64#include "viewport.h" 64#include "viewport.h"
65#include "filefuncs.h" 65#include "pathfuncs.h"
66#include "shortcuts.h" 66#include "shortcuts.h"
67 67
68static int context; 68static int context;
69static const char* selected_file = NULL; 69static const char *selected_file = NULL;
70static int selected_file_attr = 0; 70static int selected_file_attr = 0;
71static int onplay_result = ONPLAY_OK; 71static int onplay_result = ONPLAY_OK;
72static char clipboard_selection[MAX_PATH];
73static int clipboard_selection_attr = 0;
74static bool clipboard_is_copy = false;
75 72
76/* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */ 73/* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */
77#define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \ 74#define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \
@@ -82,6 +79,63 @@ static bool clipboard_is_copy = false;
82 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ 79 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
83 { (void*)name##_},{.callback_and_desc = & name##__}}; 80 { (void*)name##_},{.callback_and_desc = & name##__}};
84 81
82/* Used for directory move, copy and delete */
83struct dirrecurse_params
84{
85 char path[MAX_PATH]; /* Buffer for full path */
86 size_t append; /* Append position in 'path' for stack push */
87};
88
89enum clipboard_op_flags
90{
91 PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */
92 PASTE_COPY = 0x01, /* Is a copy operation */
93 PASTE_OVERWRITE = 0x02, /* Overwrite destination */
94 PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */
95};
96
97/* result codec of various onplay operations */
98enum onplay_result_code
99{
100 /* Anything < 0 is failure */
101 OPRC_SUCCESS = 0, /* All operations completed successfully */
102 OPRC_NOOP = 1, /* Operation didn't need to do anything */
103 OPRC_CANCELLED = 2, /* Operation was cancelled by user */
104 OPRC_NOOVERWRT = 3,
105};
106
107static struct clipboard
108{
109 char path[MAX_PATH]; /* Clipped file's path */
110 unsigned int attr; /* Clipped file's attributes */
111 unsigned int flags; /* Operation type flags */
112} clipboard;
113
114/* Empty the clipboard */
115static void clipboard_clear_selection(struct clipboard *clip)
116{
117 clip->path[0] = '\0';
118 clip->attr = 0;
119 clip->flags = 0;
120}
121
122/* Store the selection in the clipboard */
123static bool clipboard_clip(struct clipboard *clip, const char *path,
124 unsigned int attr, unsigned int flags)
125{
126 /* if it fits it clips */
127 if (strlcpy(clip->path, path, sizeof (clip->path))
128 < sizeof (clip->path)) {
129 clip->attr = attr;
130 clip->flags = flags;
131 return true;
132 }
133 else {
134 clipboard_clear_selection(clip);
135 return false;
136 }
137}
138
85/* ----------------------------------------------------------------------- */ 139/* ----------------------------------------------------------------------- */
86/* Displays the bookmark menu options for the user to decide. This is an */ 140/* Displays the bookmark menu options for the user to decide. This is an */
87/* interface function. */ 141/* interface function. */
@@ -492,438 +546,578 @@ static void draw_slider(void)
492#define draw_slider() 546#define draw_slider()
493#endif 547#endif
494 548
495/* helper function to remove a non-empty directory */ 549static void clear_display(bool update)
496static int remove_dir(char* dirname, int len)
497{ 550{
498 int result = 0; 551 struct viewport vp;
499 DIR* dir;
500 int dirlen = strlen(dirname);
501 552
502 dir = opendir(dirname); 553 FOR_NB_SCREENS(i)
503 if (!dir) 554 {
555 struct screen * screen = &screens[i];
556 viewport_set_defaults(&vp, screen->screen_type);
557 screen->set_viewport(&vp);
558 screen->clear_viewport();
559 if (update) {
560 screen->update_viewport();
561 }
562 screen->set_viewport(NULL);
563 }
564}
565
566static void splash_path(const char *path)
567{
568 clear_display(false);
569 path_basename(path, &path);
570 splash(0, path);
571 draw_slider();
572}
573
574/* Splashes the path and checks the keys */
575static bool poll_cancel_action(const char *path)
576{
577 splash_path(path);
578 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
579}
580
581static int confirm_overwrite(void)
582{
583 static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) };
584 static const struct text_message message = { lines, 1 };
585 return gui_syncyesno_run(&message, NULL, NULL);
586}
587
588static int confirm_delete(const char *file)
589{
590 const char *lines[] = { ID2P(LANG_REALLY_DELETE), file };
591 const char *yes_lines[] = { ID2P(LANG_DELETING), file };
592 const struct text_message message = { lines, 2 };
593 const struct text_message yes_message = { yes_lines, 2 };
594 return gui_syncyesno_run(&message, &yes_message, NULL);
595}
596
597static bool check_new_name(const char *basename)
598{
599 /* at least prevent escapes out of the base directory from keyboard-
600 entered filenames; the file code should reject other invalidities */
601 return *basename != '\0' && !strchr(basename, PATH_SEPCH) &&
602 !is_dotdir_name(basename);
603}
604
605static void splash_cancelled(void)
606{
607 clear_display(true);
608 splash(HZ, ID2P(LANG_CANCEL));
609}
610
611static void splash_failed(int lang_what)
612{
613 cond_talk_ids_fq(lang_what, LANG_FAILED);
614 clear_display(true);
615 splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED));
616}
617
618/* helper function to remove a non-empty directory */
619static int remove_dir(struct dirrecurse_params *parm)
620{
621 DIR *dir = opendir(parm->path);
622 if (!dir) {
504 return -1; /* open error */ 623 return -1; /* open error */
624 }
505 625
506 while(true) 626 size_t append = parm->append;
507 { 627 int rc = OPRC_SUCCESS;
508 struct dirent* entry; 628
509 /* walk through the directory content */ 629 /* walk through the directory content */
510 entry = readdir(dir); 630 while (rc == OPRC_SUCCESS) {
511 if (!entry) 631 errno = 0; /* distinguish failure from eod */
632 struct dirent *entry = readdir(dir);
633 if (!entry) {
634 if (errno) {
635 rc = -1;
636 }
512 break; 637 break;
638 }
639
513 struct dirinfo info = dir_get_info(dir, entry); 640 struct dirinfo info = dir_get_info(dir, entry);
514 dirname[dirlen] ='\0'; 641 if ((info.attribute & ATTR_DIRECTORY) &&
515 /* inform the user which dir we're deleting */ 642 is_dotdir_name(entry->d_name)) {
516 splash(0, dirname); 643 continue; /* skip these */
644 }
517 645
518 /* append name to current directory */ 646 /* append name to current directory */
519 snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); 647 parm->append = append + path_append(&parm->path[append],
520 if (info.attribute & ATTR_DIRECTORY) 648 PA_SEP_HARD, entry->d_name,
521 { /* remove a subdirectory */ 649 sizeof (parm->path) - append);
522 if (!strcmp((char *)entry->d_name, ".") || 650 if (parm->append >= sizeof (parm->path)) {
523 !strcmp((char *)entry->d_name, "..")) 651 rc = -1;
524 continue; /* skip these */ 652 break; /* no space left in buffer */
525
526 result = remove_dir(dirname, len); /* recursion */
527 if (result)
528 break; /* or better continue, delete what we can? */
529 }
530 else
531 { /* remove a file */
532 draw_slider();
533 result = remove(dirname);
534 } 653 }
535 if(ACTION_STD_CANCEL == get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) 654
536 { 655 if (info.attribute & ATTR_DIRECTORY) {
537 splash(HZ, ID2P(LANG_CANCEL)); 656 /* remove a subdirectory */
538 result = -1; 657 rc = remove_dir(parm);
539 break; 658 } else {
659 /* remove a file */
660 if (poll_cancel_action(parm->path)) {
661 rc = OPRC_CANCELLED;
662 break;
663 }
664
665 rc = remove(parm->path);
540 } 666 }
667
668 /* Remove basename we added above */
669 parm->path[append] = '\0';
541 } 670 }
542 closedir(dir);
543 671
544 if (!result) 672 closedir(dir);
545 { /* remove the now empty directory */
546 dirname[dirlen] = '\0'; /* terminate to original length */
547 673
548 result = rmdir(dirname); 674 if (rc == 0) {
675 /* remove the now empty directory */
676 if (poll_cancel_action(parm->path)) {
677 rc = OPRC_CANCELLED;
678 } else {
679 rc = rmdir(parm->path);
680 }
549 } 681 }
550 682
551 return result; 683 return rc;
552} 684}
553 685
554
555/* share code for file and directory deletion, saves space */ 686/* share code for file and directory deletion, saves space */
556static bool delete_file_dir(void) 687static int delete_file_dir(void)
557{ 688{
558 char file_to_delete[MAX_PATH]; 689 if (confirm_delete(selected_file) != YESNO_YES) {
559 strcpy(file_to_delete, selected_file); 690 return 1;
691 }
560 692
561 const char *lines[]={ 693 clear_display(true);
562 ID2P(LANG_REALLY_DELETE), 694 splash(HZ/2, str(LANG_DELETING));
563 file_to_delete
564 };
565 const char *yes_lines[]={
566 ID2P(LANG_DELETING),
567 file_to_delete
568 };
569 695
570 const struct text_message message={lines, 2}; 696 int rc = -1;
571 const struct text_message yes_message={yes_lines, 2};
572 697
573 if(gui_syncyesno_run(&message, &yes_message, NULL)!=YESNO_YES) 698 if (selected_file_attr & ATTR_DIRECTORY) { /* true if directory */
574 return false; 699 struct dirrecurse_params parm;
700 parm.append = strlcpy(parm.path, selected_file, sizeof (parm.path));
575 701
576 splash(0, str(LANG_DELETING)); 702 if (parm.append < sizeof (parm.path)) {
703 cpu_boost(true);
704 rc = remove_dir(&parm);
705 cpu_boost(false);
706 }
707 } else {
708 rc = remove(selected_file);
709 }
577 710
578 int res; 711 if (rc < OPRC_SUCCESS) {
579 if (selected_file_attr & ATTR_DIRECTORY) /* true if directory */ 712 splash_failed(LANG_DELETE);
580 { 713 } else if (rc == OPRC_CANCELLED) {
581 char pathname[MAX_PATH]; /* space to go deep */ 714 splash_cancelled();
582 cpu_boost(true);
583 strlcpy(pathname, file_to_delete, sizeof(pathname));
584 res = remove_dir(pathname, sizeof(pathname));
585 cpu_boost(false);
586 } 715 }
587 else
588 res = remove(file_to_delete);
589 716
590 if (!res) 717 if (rc != OPRC_NOOP) {
718 /* Could have failed after some but not all needed changes; reload */
591 onplay_result = ONPLAY_RELOAD_DIR; 719 onplay_result = ONPLAY_RELOAD_DIR;
720 }
592 721
593 return (res == 0); 722 return 1;
594} 723}
595 724
596static bool rename_file(void) 725static int rename_file(void)
597{ 726{
727 int rc = -1;
598 char newname[MAX_PATH]; 728 char newname[MAX_PATH];
599 char* ptr = strrchr(selected_file, '/') + 1; 729 const char *oldbase, *selection = selected_file;
600 int pathlen = (ptr - selected_file); 730
601 strlcpy(newname, selected_file, sizeof(newname)); 731 path_basename(selection, &oldbase);
602 if (!kbd_input(newname + pathlen, (sizeof newname)-pathlen)) { 732 size_t pathlen = oldbase - selection;
603 if (!strlen(newname + pathlen) || 733 char *newbase = newname + pathlen;
604 (rename(selected_file, newname) < 0)) { 734
605 cond_talk_ids_fq(LANG_RENAME, LANG_FAILED); 735 if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) {
606 splashf(HZ*2, "%s %s", str(LANG_RENAME), str(LANG_FAILED)); 736 /* Too long */
737 } else if (kbd_input(newbase, sizeof (newname) - pathlen) < 0) {
738 rc = OPRC_CANCELLED;
739 } else if (!strcmp(oldbase, newbase)) {
740 rc = OPRC_NOOP; /* No change at all */
741 } else if (check_new_name(newbase)) {
742 switch (relate(selection, newname))
743 {
744 case RELATE_DIFFERENT:
745 if (file_exists(newname)) {
746 break; /* don't overwrite */
747 }
748 /* Fall-through */
749 case RELATE_SAME:
750 rc = rename(selection, newname);
751 break;
752 case RELATE_PREFIX:
753 default:
754 break;
607 } 755 }
608 else
609 onplay_result = ONPLAY_RELOAD_DIR;
610 } 756 }
611 757
612 return false; 758 if (rc < OPRC_SUCCESS) {
759 splash_failed(LANG_RENAME);
760 } else if (rc == OPRC_CANCELLED) {
761 /* splash_cancelled(); kbd_input() splashes it */
762 } else if (rc == OPRC_SUCCESS) {
763 onplay_result = ONPLAY_RELOAD_DIR;
764 }
765
766 return 1;
613} 767}
614 768
615static bool create_dir(void) 769static int create_dir(void)
616{ 770{
771 int rc = -1;
617 char dirname[MAX_PATH]; 772 char dirname[MAX_PATH];
618 char *cwd; 773 size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD,
619 int rc; 774 sizeof (dirname));
620 int pathlen; 775 char *basename = dirname + pathlen;
621 776
622 cwd = getcwd(NULL, 0); 777 if (pathlen >= sizeof (dirname)) {
623 memset(dirname, 0, sizeof dirname); 778 /* Too long */
624 779 } else if (kbd_input(basename, sizeof (dirname) - pathlen) < 0) {
625 snprintf(dirname, sizeof dirname, "%s/", cwd[1] ? cwd : ""); 780 rc = OPRC_CANCELLED;
626 781 } else if (check_new_name(basename)) {
627 pathlen = strlen(dirname); 782 rc = mkdir(dirname);
628 rc = kbd_input(dirname + pathlen, (sizeof dirname)-pathlen); 783 }
629 if (rc < 0)
630 return false;
631 784
632 rc = mkdir(dirname); 785 if (rc < OPRC_SUCCESS) {
633 if (rc < 0) { 786 splash_failed(LANG_CREATE_DIR);
634 cond_talk_ids_fq(LANG_CREATE_DIR, LANG_FAILED); 787 } else if (rc == OPRC_CANCELLED) {
635 splashf(HZ, (unsigned char *)"%s %s", str(LANG_CREATE_DIR), 788 /* splash_cancelled(); kbd_input() splashes it */
636 str(LANG_FAILED)); 789 } else if (rc == OPRC_SUCCESS) {
637 } else {
638 onplay_result = ONPLAY_RELOAD_DIR; 790 onplay_result = ONPLAY_RELOAD_DIR;
639 } 791 }
640 792
641 return true; 793 return 1;
642} 794}
643 795
644/* Store the current selection in the clipboard */ 796/* Paste a file */
645static bool clipboard_clip(bool copy) 797static int clipboard_pastefile(const char *src, const char *target,
798 unsigned int flags)
646{ 799{
647 clipboard_selection[0] = 0; 800 int rc = -1;
648 strlcpy(clipboard_selection, selected_file, sizeof(clipboard_selection)); 801
649 clipboard_selection_attr = selected_file_attr; 802 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
650 clipboard_is_copy = copy; 803 if ((flags & PASTE_OVERWRITE) || !file_exists(target)) {
651 804 /* Rename and possibly overwrite the file */
652 return true; 805 if (poll_cancel_action(src)) {
653} 806 rc = OPRC_CANCELLED;
807 } else {
808 rc = rename(src, target);
809 }
654 810
655static bool clipboard_cut(void) 811 #ifdef HAVE_MULTIVOLUME
656{ 812 if (rc < 0 && errno == EXDEV) {
657 return clipboard_clip(false); 813 /* Failed because cross volume rename doesn't work; force
658} 814 a move instead */
815 flags |= PASTE_EXDEV;
816 break;
817 }
818 #endif /* HAVE_MULTIVOLUME */
819 }
659 820
660static bool clipboard_copy(void) 821 return rc;
661{ 822 }
662 return clipboard_clip(true);
663}
664 823
665/* Paste a file to a new directory. Will overwrite always. */ 824 /* See if we can get the plugin buffer for the file copy buffer */
666static bool clipboard_pastefile(const char *src, const char *target, bool copy)
667{
668 int src_fd, target_fd;
669 size_t buffersize; 825 size_t buffersize;
670 ssize_t size, bytesread, byteswritten; 826 char *buffer = (char *) plugin_get_buffer(&buffersize);
671 char *buffer; 827 if (buffer == NULL || buffersize < 512) {
672 bool result = false; 828 /* Not large enough, try for a disk sector worth of stack
673 829 instead */
674 if (copy) { 830 buffersize = 512;
675 /* See if we can get the plugin buffer for the file copy buffer */ 831 buffer = (char *)alloca(buffersize);
676 buffer = (char *) plugin_get_buffer(&buffersize); 832 }
677 if (buffer == NULL || buffersize < 512) {
678 /* Not large enough, try for a disk sector worth of stack
679 instead */
680 buffersize = 512;
681 buffer = (char *) __builtin_alloca(buffersize);
682 }
683 833
684 if (buffer == NULL) { 834 if (buffer == NULL) {
685 return false; 835 return -1;
686 } 836 }
687 837
688 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector 838 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
689 size */ 839 size */
690 840
691 src_fd = open(src, O_RDONLY); 841 int src_fd = open(src, O_RDONLY);
842 if (src_fd >= 0) {
843 int oflag = O_WRONLY|O_CREAT;
692 844
693 if (src_fd >= 0) { 845 if (!(flags & PASTE_OVERWRITE)) {
694 target_fd = creat(target, 0666); 846 oflag |= O_EXCL;
847 }
695 848
696 if (target_fd >= 0) { 849 int target_fd = open(target, oflag, 0666);
697 result = true; 850 if (target_fd >= 0) {
851 off_t total_size = 0;
852 off_t next_cancel_test = 0; /* No excessive button polling */
698 853
699 size = filesize(src_fd); 854 rc = OPRC_SUCCESS;
700 855
701 if (size == -1) { 856 while (rc == OPRC_SUCCESS) {
702 result = false; 857 if (total_size >= next_cancel_test) {
858 next_cancel_test = total_size + 0x10000;
859 if (poll_cancel_action(src)) {
860 rc = OPRC_CANCELLED;
861 break;
862 }
703 } 863 }
704 864
705 while(size > 0) { 865 ssize_t bytesread = read(src_fd, buffer, buffersize);
706 bytesread = read(src_fd, buffer, buffersize); 866 if (bytesread <= 0) {
707 867 if (bytesread < 0) {
708 if (bytesread == -1) { 868 rc = -1;
709 result = false;
710 break;
711 } 869 }
870 /* else eof on buffer boundary; nothing to write */
871 break;
872 }
712 873
713 size -= bytesread; 874 ssize_t byteswritten = write(target_fd, buffer, bytesread);
714 875 if (byteswritten < bytesread) {
715 while(bytesread > 0) { 876 /* Some I/O error */
716 byteswritten = write(target_fd, buffer, bytesread); 877 rc = -1;
717 878 break;
718 if (byteswritten < 0) {
719 result = false;
720 size = 0;
721 break;
722 }
723
724 bytesread -= byteswritten;
725 draw_slider();
726 }
727 } 879 }
728 880
729 close(target_fd); 881 total_size += byteswritten;
730 882
731 /* Copy failed. Cleanup. */ 883 if (bytesread < (ssize_t)buffersize) {
732 if (!result) { 884 /* EOF with trailing bytes */
733 remove(target); 885 break;
734 } 886 }
735 } 887 }
736 888
737 close(src_fd); 889 if (rc == OPRC_SUCCESS) {
738 } 890 /* If overwriting, set the correct length if original was
739 } else { 891 longer */
740 result = rename(src, target) == 0; 892 rc = ftruncate(target_fd, total_size);
741#ifdef HAVE_MULTIVOLUME 893 }
742 if (!result) { 894
743 if (errno == EXDEV) { 895 close(target_fd);
744 /* Failed because cross volume rename doesn't work. Copy 896
745 instead */ 897 if (rc != OPRC_SUCCESS) {
746 result = clipboard_pastefile(src, target, true); 898 /* Copy failed. Cleanup. */
747 899 remove(target);
748 if (result) {
749 result = remove(src) == 0;
750 }
751 } 900 }
752 } 901 }
753#endif 902
903 close(src_fd);
754 } 904 }
755 905
756 return result; 906 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
907 /* Remove the source file */
908 rc = remove(src);
909 }
910
911 return rc;
757} 912}
758 913
759/* Paste a directory to a new location. Designed to be called by 914/* Paste a directory */
760 clipboard_paste */ 915static int clipboard_pastedirectory(struct dirrecurse_params *src,
761static bool clipboard_pastedirectory(char *src, int srclen, char *target, 916 struct dirrecurse_params *target,
762 int targetlen, bool copy) 917 unsigned int flags)
763{ 918{
764 DIR *srcdir; 919 int rc = -1;
765 int srcdirlen = strlen(src); 920
766 int targetdirlen = strlen(target); 921 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
767 bool result = true; 922 if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) {
768 923 /* Just try to move the directory */
769 if (!file_exists(target)) { 924 if (poll_cancel_action(src->path)) {
770 if (!copy) { 925 rc = OPRC_CANCELLED;
771 /* Just move the directory */ 926 } else {
772 result = rename(src, target) == 0; 927 rc = rename(src->path, target->path);
928 }
773 929
774#ifdef HAVE_MULTIVOLUME 930 if (rc < 0) {
775 if (!result && errno == EXDEV) { 931 int errnum = errno;
776 /* Try a copy as we're going across devices */ 932 if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
777 result = clipboard_pastedirectory(src, srclen, target, 933 /* Directory is not empty thus rename() will not do a quick
778 targetlen, true); 934 overwrite */
779 935 break;
780 /* If it worked, remove the source directory */
781 if (result) {
782 remove_dir(src, srclen);
783 } 936 }
937 #ifdef HAVE_MULTIVOLUME
938 else if (errnum == EXDEV) {
939 /* Failed because cross volume rename doesn't work; force
940 a move instead */
941 flags |= PASTE_EXDEV;
942 break;
943 }
944 #endif /* HAVE_MULTIVOLUME */
784 } 945 }
785#endif
786 return result;
787 } else {
788 /* Make a directory to copy things to */
789 result = mkdir(target) == 0;
790 } 946 }
791 }
792 947
793 /* Check if something went wrong already */ 948 return rc;
794 if (!result) {
795 return result;
796 } 949 }
797 950
798 srcdir = opendir(src); 951 DIR *srcdir = opendir(src->path);
799 if (!srcdir) { 952
800 return false; 953 if (srcdir) {
954 /* Make a directory to copy things to */
955 rc = mkdir(target->path);
956 if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) {
957 /* Exists and overwrite was approved */
958 rc = OPRC_SUCCESS;
959 }
801 } 960 }
802 961
803 /* This loop will exit as soon as there's a problem */ 962 size_t srcap = src->append, targetap = target->append;
804 while(result) 963
805 { 964 /* Walk through the directory content; this loop will exit as soon as
806 struct dirent* entry; 965 there's a problem */
807 /* walk through the directory content */ 966 while (rc == OPRC_SUCCESS) {
808 entry = readdir(srcdir); 967 errno = 0; /* Distinguish failure from eod */
809 if (!entry) 968 struct dirent *entry = readdir(srcdir);
969 if (!entry) {
970 if (errno) {
971 rc = -1;
972 }
810 break; 973 break;
974 }
811 975
812 struct dirinfo info = dir_get_info(srcdir, entry); 976 struct dirinfo info = dir_get_info(srcdir, entry);
813 /* append name to current directory */ 977 if ((info.attribute & ATTR_DIRECTORY) &&
814 snprintf(src+srcdirlen, srclen-srcdirlen, "/%s", entry->d_name); 978 is_dotdir_name(entry->d_name)) {
815 snprintf(target+targetdirlen, targetlen-targetdirlen, "/%s", 979 continue; /* Skip these */
816 entry->d_name); 980 }
817 981
818 DEBUGF("Copy %s to %s\n", src, target); 982 /* Append names to current directories */
983 src->append = srcap +
984 path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name,
985 sizeof(src->path) - srcap);
819 986
820 if (info.attribute & ATTR_DIRECTORY) 987 target->append = targetap +
821 { /* copy/move a subdirectory */ 988 path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name,
822 if (!strcmp((char *)entry->d_name, ".") || 989 sizeof (target->path) - targetap);
823 !strcmp((char *)entry->d_name, ".."))
824 continue; /* skip these */
825 990
826 result = clipboard_pastedirectory(src, srclen, target, targetlen, 991 if (src->append >= sizeof (src->path) ||
827 copy); /* recursion */ 992 target->append >= sizeof (target->path)) {
993 rc = -1; /* No space left in buffer */
994 break;
828 } 995 }
829 else 996
830 { /* copy/move a file */ 997 if (poll_cancel_action(src->path)) {
831 draw_slider(); 998 rc = OPRC_CANCELLED;
832 result = clipboard_pastefile(src, target, copy); 999 break;
1000 }
1001
1002 DEBUGF("Copy %s to %s\n", src->path, target->path);
1003
1004 if (info.attribute & ATTR_DIRECTORY) {
1005 /* Copy/move a subdirectory */
1006 rc = clipboard_pastedirectory(src, target, flags); /* recursion */
1007 } else {
1008 /* Copy/move a file */
1009 rc = clipboard_pastefile(src->path, target->path, flags);
833 } 1010 }
1011
1012 /* Remove basenames we added above */
1013 src->path[srcap] = target->path[targetap] = '\0';
1014 }
1015
1016 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
1017 /* Remove the now empty directory */
1018 rc = rmdir(src->path);
834 } 1019 }
835 1020
836 closedir(srcdir); 1021 closedir(srcdir);
1022 return rc;
1023}
837 1024
838 if (result) { 1025static bool clipboard_cut(void)
839 src[srcdirlen] = '\0'; /* terminate to original length */ 1026{
840 target[targetdirlen] = '\0'; /* terminate to original length */ 1027 return clipboard_clip(&clipboard, selected_file, selected_file_attr,
841 } 1028 PASTE_CUT);
1029}
842 1030
843 return result; 1031static bool clipboard_copy(void)
1032{
1033 return clipboard_clip(&clipboard, selected_file, selected_file_attr,
1034 PASTE_COPY);
844} 1035}
845 1036
846/* Paste the clipboard to the current directory */ 1037/* Paste the clipboard to the current directory */
847static bool clipboard_paste(void) 1038static int clipboard_paste(void)
848{ 1039{
849 char target[MAX_PATH]; 1040 if (!clipboard.path[0])
850 char *cwd, *nameptr; 1041 return 1;
851 bool success;
852 1042
853 static const char *lines[]={ID2P(LANG_REALLY_OVERWRITE)}; 1043 int rc = -1;
854 static const struct text_message message={lines, 1};
855 1044
856 /* Get the name of the current directory */ 1045 struct dirrecurse_params src, target;
857 cwd = getcwd(NULL, 0); 1046 unsigned int flags = clipboard.flags;
858 1047
859 /* Figure out the name of the selection */ 1048 /* Figure out the name of the selection */
860 nameptr = strrchr(clipboard_selection, '/'); 1049 const char *nameptr;
1050 path_basename(clipboard.path, &nameptr);
861 1051
862 /* Final target is current directory plus name of selection */ 1052 /* Final target is current directory plus name of selection */
863 snprintf(target, sizeof(target), "%s%s", cwd[1] ? cwd : "", nameptr); 1053 target.append = path_append(target.path, getcwd(NULL, 0),
864 1054 nameptr, sizeof (target.path));
865 /* If the target existed but they choose not to overwite, exit */
866 if (file_exists(target) &&
867 (gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO)) {
868 return false;
869 }
870 1055
871 if (clipboard_is_copy) { 1056 switch (target.append < sizeof (target.path) ?
872 splash(0, ID2P(LANG_COPYING)); 1057 relate(clipboard.path, target.path) : -1)
873 }
874 else
875 { 1058 {
876 splash(0, ID2P(LANG_MOVING)); 1059 case RELATE_SAME:
877 } 1060 rc = OPRC_NOOP;
1061 break;
1062
1063 case RELATE_DIFFERENT:
1064 if (file_exists(target.path)) {
1065 /* If user chooses not to overwrite, cancel */
1066 if (confirm_overwrite() == YESNO_NO) {
1067 rc = OPRC_NOOVERWRT;
1068 break;
1069 }
878 1070
879 /* Now figure out what we're doing */ 1071 flags |= PASTE_OVERWRITE;
880 cpu_boost(true);
881 if (clipboard_selection_attr & ATTR_DIRECTORY) {
882 /* Recursion. Set up external stack */
883 char srcpath[MAX_PATH];
884 char targetpath[MAX_PATH];
885 if (!strncmp(clipboard_selection, target, strlen(clipboard_selection)))
886 {
887 /* Do not allow the user to paste a directory into a dir they are
888 copying */
889 success = 0;
890 } 1072 }
891 else
892 {
893 strlcpy(srcpath, clipboard_selection, sizeof(srcpath));
894 strlcpy(targetpath, target, sizeof(targetpath));
895 1073
896 success = clipboard_pastedirectory(srcpath, sizeof(srcpath), 1074 clear_display(true);
897 target, sizeof(targetpath), clipboard_is_copy); 1075 splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) :
1076 ID2P(LANG_MOVING));
898 1077
899 if (success && !clipboard_is_copy) 1078 /* Now figure out what we're doing */
900 { 1079 cpu_boost(true);
901 strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); 1080
902 remove_dir(srcpath, sizeof(srcpath)); 1081 if (clipboard.attr & ATTR_DIRECTORY) {
1082 /* Copy or move a subdirectory */
1083 src.append = strlcpy(src.path, clipboard.path,
1084 sizeof (src.path));
1085 if (src.append < sizeof (src.path)) {
1086 rc = clipboard_pastedirectory(&src, &target, flags);
903 } 1087 }
1088 } else {
1089 /* Copy or move a file */
1090 rc = clipboard_pastefile(clipboard.path, target.path, flags);
904 } 1091 }
905 } else { 1092
906 success = clipboard_pastefile(clipboard_selection, target, 1093 cpu_boost(false);
907 clipboard_is_copy); 1094 break;
1095
1096 case RELATE_PREFIX:
1097 default: /* Some other relation / failure */
1098 break;
908 } 1099 }
909 cpu_boost(false);
910 1100
911 /* Did it work? */ 1101 clear_display(true);
912 if (success) {
913 /* Reset everything */
914 clipboard_selection[0] = 0;
915 clipboard_selection_attr = 0;
916 clipboard_is_copy = false;
917 1102
918 /* Force reload of the current directory */ 1103 switch (rc)
1104 {
1105 case OPRC_CANCELLED:
1106 splash_cancelled();
1107 case OPRC_SUCCESS:
919 onplay_result = ONPLAY_RELOAD_DIR; 1108 onplay_result = ONPLAY_RELOAD_DIR;
920 } else { 1109 case OPRC_NOOP:
921 cond_talk_ids_fq(LANG_PASTE, LANG_FAILED); 1110 clipboard_clear_selection(&clipboard);
922 splashf(HZ, (unsigned char *)"%s %s", str(LANG_PASTE), 1111 case OPRC_NOOVERWRT:
923 str(LANG_FAILED)); 1112 break;
1113 default:
1114 if (rc < OPRC_SUCCESS) {
1115 splash_failed(LANG_PASTE);
1116 onplay_result = ONPLAY_RELOAD_DIR;
1117 }
924 } 1118 }
925 1119
926 return true; 1120 return 1;
927} 1121}
928 1122
929#ifdef HAVE_TAGCACHE 1123#ifdef HAVE_TAGCACHE
@@ -1094,15 +1288,12 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item)
1094 { 1288 {
1095 case ACTION_REQUEST_MENUITEM: 1289 case ACTION_REQUEST_MENUITEM:
1096#ifdef HAVE_MULTIVOLUME 1290#ifdef HAVE_MULTIVOLUME
1097 if ((selected_file_attr & FAT_ATTR_VOLUME) &&
1098 (this_item == &rename_file_item ||
1099 this_item == &delete_dir_item ||
1100 this_item == &clipboard_cut_item) )
1101 return ACTION_EXIT_MENUITEM;
1102 /* no rename+delete for volumes */ 1291 /* no rename+delete for volumes */
1103 if ((selected_file_attr & ATTR_VOLUME) && 1292 if ((selected_file_attr & ATTR_VOLUME) &&
1104 (this_item == &delete_file_item || 1293 (this_item == &rename_file_item ||
1105 this_item == &list_viewers_item)) 1294 this_item == &delete_dir_item ||
1295 this_item == &clipboard_cut_item ||
1296 this_item == &list_viewers_item))
1106 return ACTION_EXIT_MENUITEM; 1297 return ACTION_EXIT_MENUITEM;
1107#endif 1298#endif
1108#ifdef HAVE_TAGCACHE 1299#ifdef HAVE_TAGCACHE
@@ -1117,7 +1308,7 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item)
1117#endif 1308#endif
1118 if (this_item == &clipboard_paste_item) 1309 if (this_item == &clipboard_paste_item)
1119 { /* visible if there is something to paste */ 1310 { /* visible if there is something to paste */
1120 return (clipboard_selection[0] != 0) ? 1311 return (clipboard.path[0] != 0) ?
1121 action : ACTION_EXIT_MENUITEM; 1312 action : ACTION_EXIT_MENUITEM;
1122 } 1313 }
1123 else if (this_item == &create_dir_item) 1314 else if (this_item == &create_dir_item)
@@ -1232,8 +1423,7 @@ static bool delete_item(void)
1232{ 1423{
1233#ifdef HAVE_MULTIVOLUME 1424#ifdef HAVE_MULTIVOLUME
1234 /* no delete for volumes */ 1425 /* no delete for volumes */
1235 if ((selected_file_attr & FAT_ATTR_VOLUME) || 1426 if (selected_file_attr & ATTR_VOLUME)
1236 (selected_file_attr & ATTR_VOLUME))
1237 return false; 1427 return false;
1238#endif 1428#endif
1239 return delete_file_dir(); 1429 return delete_file_dir();
diff --git a/apps/playlist.c b/apps/playlist.c
index 43aa97790b..db93344ef1 100755
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -86,7 +86,7 @@
86#include "screens.h" 86#include "screens.h"
87#include "core_alloc.h" 87#include "core_alloc.h"
88#include "misc.h" 88#include "misc.h"
89#include "filefuncs.h" 89#include "pathfuncs.h"
90#include "button.h" 90#include "button.h"
91#include "filetree.h" 91#include "filetree.h"
92#include "abrepeat.h" 92#include "abrepeat.h"
@@ -107,6 +107,8 @@
107#include "panic.h" 107#include "panic.h"
108#include "logdiskf.h" 108#include "logdiskf.h"
109 109
110#undef HAVE_DIRCACHE
111
110#define PLAYLIST_CONTROL_FILE_VERSION 2 112#define PLAYLIST_CONTROL_FILE_VERSION 2
111 113
112/* 114/*
@@ -180,8 +182,8 @@ static int get_next_directory(char *dir);
180static int get_next_dir(char *dir, bool is_forward); 182static int get_next_dir(char *dir, bool is_forward);
181static int get_previous_directory(char *dir); 183static int get_previous_directory(char *dir);
182static int check_subdir_for_music(char *dir, const char *subdir, bool recurse); 184static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
183static int format_track_path(char *dest, char *src, int buf_length, int max, 185static ssize_t format_track_path(char *dest, char *src, int buf_length,
184 const char *dir); 186 const char *dir);
185static void display_playlist_count(int count, const unsigned char *fmt, 187static void display_playlist_count(int count, const unsigned char *fmt,
186 bool final); 188 bool final);
187static void display_buffer_full(void); 189static void display_buffer_full(void);
@@ -526,7 +528,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
526 int result = 0; 528 int result = 0;
527 /* get emergency buffer so we don't fail horribly */ 529 /* get emergency buffer so we don't fail horribly */
528 if (!buflen) 530 if (!buflen)
529 buffer = __builtin_alloca((buflen = 64)); 531 buffer = alloca((buflen = 64));
530 532
531 if(-1 == playlist->fd) 533 if(-1 == playlist->fd)
532 playlist->fd = open_utf8(playlist->filename, O_RDONLY); 534 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
@@ -1429,7 +1431,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek,
1429 1431
1430 strlcpy(dir_buf, playlist->filename, playlist->dirlen); 1432 strlcpy(dir_buf, playlist->filename, playlist->dirlen);
1431 1433
1432 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf)); 1434 return format_track_path(buf, tmp_buf, buf_length, dir_buf);
1433} 1435}
1434 1436
1435static int get_next_directory(char *dir){ 1437static int get_next_directory(char *dir){
@@ -1629,7 +1631,7 @@ static int get_next_dir(char *dir, bool is_forward)
1629static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) 1631static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1630{ 1632{
1631 int result = -1; 1633 int result = -1;
1632 int dirlen = strlen(dir); 1634 size_t dirlen = strlen(dir);
1633 int num_files = 0; 1635 int num_files = 0;
1634 int i; 1636 int i;
1635 struct entry *files; 1637 struct entry *files;
@@ -1637,12 +1639,11 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1637 bool has_subdir = false; 1639 bool has_subdir = false;
1638 struct tree_context* tc = tree_get_context(); 1640 struct tree_context* tc = tree_get_context();
1639 1641
1640 snprintf( 1642 if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
1641 dir + dirlen, MAX_PATH - dirlen, 1643 MAX_PATH - dirlen)
1642 /* only add a trailing slash if we need one */ 1644 {
1643 dirlen && dir[dirlen - 1] == '/' ? "%s" : "/%s", 1645 return 0;
1644 subdir 1646 }
1645 );
1646 1647
1647 if (ft_load(tc, dir) < 0) 1648 if (ft_load(tc, dir) < 0)
1648 { 1649 {
@@ -1695,7 +1696,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1695 } 1696 }
1696 else 1697 else
1697 { 1698 {
1698 strcpy(dir, "/"); 1699 strcpy(dir, PATH_ROOTSTR);
1699 } 1700 }
1700 1701
1701 /* we now need to reload our current directory */ 1702 /* we now need to reload our current directory */
@@ -1708,79 +1709,31 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1708/* 1709/*
1709 * Returns absolute path of track 1710 * Returns absolute path of track
1710 */ 1711 */
1711static int format_track_path(char *dest, char *src, int buf_length, int max, 1712static ssize_t format_track_path(char *dest, char *src, int buf_length,
1712 const char *dir) 1713 const char *dir)
1713{ 1714{
1714 int i = 0; 1715 size_t len;
1715 int j;
1716 char *temp_ptr;
1717
1718 /* Look for the end of the string */
1719 while((i < max) &&
1720 (src[i] != '\n') &&
1721 (src[i] != '\r') &&
1722 (src[i] != '\0'))
1723 i++;
1724
1725 /* Now work back killing white space */
1726 while((i > 0) &&
1727 ((src[i-1] == ' ') ||
1728 (src[i-1] == '\t')))
1729 i--;
1730 1716
1731 /* Zero-terminate the file name */ 1717 /* strip whitespace at beginning and end */
1732 src[i]=0; 1718 len = path_trim_whitespace(src, (const char **)&src);
1719 src[len] = '\0';
1733 1720
1734 /* replace backslashes with forward slashes */ 1721 /* replace backslashes with forward slashes */
1735 for ( j=0; j<i; j++ ) 1722 path_correct_separators(src, src);
1736 if ( src[j] == '\\' )
1737 src[j] = '/';
1738 1723
1739 if('/' == src[0]) 1724 /* handle DOS style drive letter and parse non-greedily so that:
1740 { 1725 * 1) "c:/foo" becomes "/foo" and the result is absolute
1741 strlcpy(dest, src, buf_length); 1726 * 2) "c:foo becomes "foo" and the result is relative
1742 } 1727 * This is how Windows seems to handle it except drive letters are of no
1743 else 1728 * meaning here. */
1744 { 1729 path_strip_drive(src, (const char **)&src, false);
1745 /* handle dos style drive letter */
1746 if (':' == src[1])
1747 strlcpy(dest, &src[2], buf_length);
1748 else if (!strncmp(src, "../", 3))
1749 {
1750 /* handle relative paths */
1751 i=3;
1752 while(!strncmp(&src[i], "../", 3))
1753 i += 3;
1754 for (j=0; j<i/3; j++) {
1755 temp_ptr = strrchr(dir, '/');
1756 if (temp_ptr)
1757 *temp_ptr = '\0';
1758 else
1759 break;
1760 }
1761 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1762 }
1763 else if ( '.' == src[0] && '/' == src[1] ) {
1764 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1765 }
1766 else {
1767 snprintf(dest, buf_length, "%s/%s", dir, src);
1768 }
1769 }
1770#ifdef HAVE_MULTIVOLUME
1771 1730
1772 char vol_string[VOL_ENUM_POS + 8]; 1731 /* prepends directory only if src is relative */
1773 snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, 1); 1732 len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
1733 if (len >= (size_t)buf_length)
1734 return -1; /* buffer too small */
1774 1735
1775 /*check if the playlist is on a external card, and correct path if needed */ 1736 return len;
1776 if(strstr(dir, vol_string) && (strstr(dest, vol_string) == NULL)){
1777 char temp[buf_length];
1778 strlcpy(temp, dest, buf_length);
1779 snprintf(dest, buf_length, "%s%s", vol_string, temp);
1780 }
1781#endif
1782
1783 return 0;
1784} 1737}
1785 1738
1786/* 1739/*
@@ -3113,8 +3066,7 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3113{ 3066{
3114 int fd; 3067 int fd;
3115 int max; 3068 int max;
3116 char *temp_ptr; 3069 char *dir;
3117 const char *dir;
3118 unsigned char *count_str; 3070 unsigned char *count_str;
3119 char temp_buf[MAX_PATH+1]; 3071 char temp_buf[MAX_PATH+1];
3120 char trackname[MAX_PATH+1]; 3072 char trackname[MAX_PATH+1];
@@ -3139,13 +3091,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3139 } 3091 }
3140 3092
3141 /* we need the directory name for formatting purposes */ 3093 /* we need the directory name for formatting purposes */
3142 dir = filename; 3094 size_t dirlen = path_dirname(filename, (const char **)&dir);
3143 3095 dir = strmemdupa(dir, dirlen);
3144 temp_ptr = strrchr(filename+1,'/');
3145 if (temp_ptr)
3146 *temp_ptr = 0;
3147 else
3148 dir = "/";
3149 3096
3150 if (queue) 3097 if (queue)
3151 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); 3098 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
@@ -3183,8 +3130,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3183 3130
3184 /* we need to format so that relative paths are correctly 3131 /* we need to format so that relative paths are correctly
3185 handled */ 3132 handled */
3186 if (format_track_path(trackname, temp_buf, sizeof(trackname), max, 3133 if (format_track_path(trackname, temp_buf, sizeof(trackname),
3187 dir) < 0) 3134 dir) < 0)
3188 { 3135 {
3189 result = -1; 3136 result = -1;
3190 break; 3137 break;
@@ -3223,9 +3170,6 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
3223 3170
3224 close(fd); 3171 close(fd);
3225 3172
3226 if (temp_ptr)
3227 *temp_ptr = '/';
3228
3229 sync_control(playlist, false); 3173 sync_control(playlist, false);
3230 3174
3231 cpu_boost(false); 3175 cpu_boost(false);
@@ -3537,9 +3481,9 @@ int playlist_save(struct playlist_info* playlist, char *filename,
3537 char path[MAX_PATH+1]; 3481 char path[MAX_PATH+1];
3538 char tmp_buf[MAX_PATH+1]; 3482 char tmp_buf[MAX_PATH+1];
3539 int result = 0; 3483 int result = 0;
3540 bool overwrite_current = false;
3541 int *seek_buf; 3484 int *seek_buf;
3542 bool reparse; 3485 bool reparse;
3486 ssize_t pathlen;
3543 3487
3544 ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int)); 3488 ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int));
3545 seek_buf = temp_buffer; 3489 seek_buf = temp_buffer;
@@ -3557,22 +3501,18 @@ int playlist_save(struct playlist_info* playlist, char *filename,
3557 return -1; 3501 return -1;
3558 3502
3559 /* use current working directory as base for pathname */ 3503 /* use current working directory as base for pathname */
3560 if (format_track_path(path, filename, sizeof(tmp_buf), 3504 pathlen = format_track_path(path, filename, sizeof(path), PATH_ROOTSTR);
3561 strlen(filename)+1, "/") < 0) 3505 if (pathlen < 0)
3506 return -1;
3507
3508 /* Use temporary pathname and overwrite/rename later */
3509 if (strlcat(path, "_temp", sizeof(path)) >= sizeof (path))
3562 return -1; 3510 return -1;
3563 3511
3564 /* can ignore volatile here, because core_get_data() is called later */ 3512 /* can ignore volatile here, because core_get_data() is called later */
3565 char* old_buffer = (char*)playlist->buffer; 3513 char* old_buffer = (char*)playlist->buffer;
3566 size_t old_buffer_size = playlist->buffer_size; 3514 size_t old_buffer_size = playlist->buffer_size;
3567 3515
3568 if (!strncmp(playlist->filename, path, strlen(path)))
3569 {
3570 /* Attempting to overwrite current playlist file.
3571 * use temporary pathname and overwrite later */
3572 strlcat(path, "_temp", sizeof(path));
3573 overwrite_current = true;
3574 }
3575
3576 if (is_m3u8(path)) 3516 if (is_m3u8(path))
3577 { 3517 {
3578 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC); 3518 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
@@ -3621,8 +3561,8 @@ int playlist_save(struct playlist_info* playlist, char *filename,
3621 break; 3561 break;
3622 } 3562 }
3623 3563
3624 if (overwrite_current && !reparse) 3564 if (!reparse)
3625 seek_buf[count] = lseek(fd, 0, SEEK_CUR); 3565 seek_buf[count] = filesize(fd);
3626 3566
3627 if (fdprintf(fd, "%s\n", tmp_buf) < 0) 3567 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3628 { 3568 {
@@ -3647,57 +3587,61 @@ int playlist_save(struct playlist_info* playlist, char *filename,
3647 index = (index+1)%playlist->amount; 3587 index = (index+1)%playlist->amount;
3648 } 3588 }
3649 3589
3650 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3651
3652 close(fd); 3590 close(fd);
3591 fd = -1;
3653 3592
3654 if (overwrite_current && result >= 0) 3593 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3594
3595 if (result >= 0)
3655 { 3596 {
3656 result = -1; 3597 strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */
3657 3598
3658 mutex_lock(playlist->control_mutex); 3599 mutex_lock(playlist->control_mutex);
3659 3600
3660 /* Replace the current playlist with the new one and update indices */ 3601 if (!rename(path, tmp_buf))
3661 close(playlist->fd);
3662 playlist->fd = -1;
3663 if (remove(playlist->filename) >= 0)
3664 { 3602 {
3665 if (rename(path, playlist->filename) >= 0) 3603 fd = open_utf8(tmp_buf, O_RDONLY);
3604 if (fsamefile(fd, playlist->fd) > 0)
3666 { 3605 {
3667 playlist->fd = open_utf8(playlist->filename, O_RDONLY); 3606 /* Replace the current playlist with the new one and update
3668 if (playlist->fd >= 0) 3607 indices */
3608 close(playlist->fd);
3609 playlist->fd = fd;
3610 fd = -1;
3611
3612 if (!reparse)
3669 { 3613 {
3670 if (!reparse) 3614 index = playlist->first_index;
3615 for (i=0, count=0; i<playlist->amount; i++)
3671 { 3616 {
3672 index = playlist->first_index; 3617 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3673 for (i=0, count=0; i<playlist->amount; i++)
3674 { 3618 {
3675 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) 3619 playlist->indices[index] = seek_buf[count];
3676 { 3620 count++;
3677 playlist->indices[index] = seek_buf[count];
3678 count++;
3679 }
3680 index = (index+1)%playlist->amount;
3681 } 3621 }
3622 index = (index+1)%playlist->amount;
3682 } 3623 }
3683 else
3684 {
3685 NOTEF("reparsing current playlist (slow)");
3686 playlist->amount = 0;
3687 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
3688 }
3689 /* we need to recreate control because inserted tracks are
3690 now part of the playlist and shuffle has been
3691 invalidated */
3692 result = recreate_control(playlist);
3693 } 3624 }
3694 } 3625 else
3695 } 3626 {
3627 NOTEF("reparsing current playlist (slow)");
3628 playlist->amount = 0;
3629 add_indices_to_playlist(playlist, temp_buffer,
3630 temp_buffer_size);
3631 }
3696 3632
3697 mutex_unlock(playlist->control_mutex); 3633 /* we need to recreate control because inserted tracks are
3634 now part of the playlist and shuffle has been invalidated */
3635 result = recreate_control(playlist);
3636 }
3637 }
3698 3638
3639 mutex_unlock(playlist->control_mutex);
3699 } 3640 }
3700 3641
3642 if (fd >= 0)
3643 close(fd);
3644
3701 cpu_boost(false); 3645 cpu_boost(false);
3702 3646
3703reset_old_buffer: 3647reset_old_buffer:
@@ -3759,8 +3703,12 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3759 if (recurse) 3703 if (recurse)
3760 { 3704 {
3761 /* recursively add directories */ 3705 /* recursively add directories */
3762 snprintf(buf, sizeof(buf), "%s/%s", 3706 if (path_append(buf, dirname, files[i].name, sizeof(buf))
3763 dirname[1]? dirname: "", files[i].name); 3707 >= sizeof(buf))
3708 {
3709 continue;
3710 }
3711
3764 result = playlist_directory_tracksearch(buf, recurse, 3712 result = playlist_directory_tracksearch(buf, recurse,
3765 callback, context); 3713 callback, context);
3766 if (result < 0) 3714 if (result < 0)
@@ -3785,8 +3733,11 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3785 } 3733 }
3786 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) 3734 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3787 { 3735 {
3788 snprintf(buf, sizeof(buf), "%s/%s", 3736 if (path_append(buf, dirname, files[i].name, sizeof(buf))
3789 dirname[1]? dirname: "", files[i].name); 3737 >= sizeof(buf))
3738 {
3739 continue;
3740 }
3790 3741
3791 if (callback(buf, context) != 0) 3742 if (callback(buf, context) != 0)
3792 { 3743 {
diff --git a/apps/playlist_catalog.c b/apps/playlist_catalog.c
index 3687681b66..5741d11258 100644
--- a/apps/playlist_catalog.c
+++ b/apps/playlist_catalog.c
@@ -32,7 +32,7 @@
32#include "lang.h" 32#include "lang.h"
33#include "list.h" 33#include "list.h"
34#include "misc.h" 34#include "misc.h"
35#include "filefuncs.h" 35#include "pathfuncs.h"
36#include "onplay.h" 36#include "onplay.h"
37#include "playlist.h" 37#include "playlist.h"
38#include "settings.h" 38#include "settings.h"
diff --git a/apps/plugin.c b/apps/plugin.c
index 8edc773239..8a6c577f69 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -18,6 +18,8 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#define DIRFUNCTIONS_DEFINED
22#define FILEFUNCTIONS_DEFINED
21#include "plugin.h" 23#include "plugin.h"
22#include <ctype.h> 24#include <ctype.h>
23#include <string.h> 25#include <string.h>
@@ -40,8 +42,9 @@
40#include "pcmbuf.h" 42#include "pcmbuf.h"
41#include "errno.h" 43#include "errno.h"
42#include "diacritic.h" 44#include "diacritic.h"
43#include "filefuncs.h" 45#include "pathfuncs.h"
44#include "load_code.h" 46#include "load_code.h"
47#include "file.h"
45 48
46#if CONFIG_CHARGING 49#if CONFIG_CHARGING
47#include "power.h" 50#include "power.h"
@@ -58,80 +61,119 @@
58#include "usbstack/usb_hid.h" 61#include "usbstack/usb_hid.h"
59#endif 62#endif
60 63
61#if defined (SIMULATOR) 64#define WRAPPER(_x_) _x_ ## _wrapper
62#define PREFIX(_x_) sim_ ## _x_ 65
63#elif defined (APPLICATION) 66#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
64#define PREFIX(_x_) app_ ## _x_ 67static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE];
68void sim_lcd_ex_init(unsigned long (*getpixel)(int, int));
69void sim_lcd_ex_update_rect(int x, int y, int width, int height);
65#else 70#else
66#define PREFIX(_x_) _x_ 71extern unsigned char pluginbuf[];
72#include "bitswap.h"
67#endif 73#endif
68 74
69#if defined (APPLICATION) 75/* for actual plugins only, not for codecs */
70/* For symmetry reasons (we want app_ and sim_ to behave similarly), some 76static int plugin_size = 0;
71 * wrappers are needed */ 77static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */
72static int app_close(int fd) 78static char current_plugin[MAX_PATH];
79/* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */
80static void *current_plugin_handle;
81
82char *plugin_get_current_filename(void);
83
84static void* plugin_get_audio_buffer(size_t *buffer_size);
85static void plugin_release_audio_buffer(void);
86static void plugin_tsr(bool (*exit_callback)(bool));
87
88
89#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
90/* File handle leak prophylaxis */
91#include "bitarray.h"
92#include "file_internal.h" /* for MAX_OPEN_FILES */
93
94#define PCOC_WRAPPER(_x_) WRAPPER(_x_)
95
96BITARRAY_TYPE_DECLARE(plugin_check_open_close_bitmap_t, open_files_bitmap,
97 MAX_OPEN_FILES)
98
99static plugin_check_open_close_bitmap_t open_files_bitmap;
100
101static void plugin_check_open_close__enter(void)
73{ 102{
74 return close(fd); 103 if (!current_plugin_handle)
104 open_files_bitmap_clear(&open_files_bitmap);
75} 105}
76 106
77static ssize_t app_read(int fd, void *buf, size_t count) 107static void plugin_check_open_close__open(int fildes)
78{ 108{
79 return read(fd,buf,count); 109 if (fildes >= 0)
110 open_files_bitmap_set_bit(&open_files_bitmap, fildes);
80} 111}
81 112
82static off_t app_lseek(int fd, off_t offset, int whence) 113static void plugin_check_open_close__close(int fildes)
83{ 114{
84 return lseek(fd,offset,whence); 115 if (fildes < 0)
116 return;
117
118 if (!open_files_bitmap_test_bit(&open_files_bitmap, fildes))
119 {
120 logf("double close from plugin");
121 }
122
123 open_files_bitmap_clear_bit(&open_files_bitmap, fildes);
85} 124}
86 125
87static ssize_t app_write(int fd, const void *buf, size_t count) 126static int WRAPPER(open)(const char *path, int oflag, ...)
88{ 127{
89 return write(fd,buf,count); 128 int fildes = FS_PREFIX(open)(path, oflag __OPEN_MODE_ARG);
129 plugin_check_open_close__open(fildes);
130 return fildes;
90} 131}
91 132
92static int app_ftruncate(int fd, off_t length) 133static int WRAPPER(creat)(const char *path, mode_t mode)
93{ 134{
94 return ftruncate(fd,length); 135 int fildes = FS_PREFIX(creat)(path __CREAT_MODE_ARG);
136 plugin_check_open_close__open(fildes);
137 return fildes;
138 (void)mode;
95} 139}
96#endif
97 140
98#if defined(HAVE_PLUGIN_CHECK_OPEN_CLOSE) && (MAX_OPEN_FILES>32) 141static int WRAPPER(close)(int fildes)
99#warning "MAX_OPEN_FILES>32, disabling plugin file open/close checking" 142{
100#undef HAVE_PLUGIN_CHECK_OPEN_CLOSE 143 int rc = FS_PREFIX(close)(fildes);
101#endif 144 if (rc >= 0)
145 plugin_check_open_close__close(fildes);
102 146
103#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 147 return rc;
104static unsigned int open_files; 148}
105#endif
106 149
107#if (CONFIG_PLATFORM & PLATFORM_HOSTED) 150static void plugin_check_open_close__exit(void)
108static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE]; 151{
109void sim_lcd_ex_init(unsigned long (*getpixel)(int, int)); 152 if (current_plugin_handle)
110void sim_lcd_ex_update_rect(int x, int y, int width, int height); 153 return;
111#else
112extern unsigned char pluginbuf[];
113#include "bitswap.h"
114#endif
115 154
116/* for actual plugins only, not for codecs */ 155 if (open_files_bitmap_is_clear(&open_files_bitmap))
117static int plugin_size = 0; 156 return;
118static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */
119static char current_plugin[MAX_PATH];
120/* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */
121static void *current_plugin_handle;
122 157
123char *plugin_get_current_filename(void); 158 logf("Plugin '%s' leaks file handles", plugin);
124 159
125/* Some wrappers used to monitor open and close and detect leaks*/ 160 static const char *lines[] =
126static int open_wrapper(const char* pathname, int flags, ...); 161 { ID2P(LANG_PLUGIN_ERROR), "#leak-file-handles" };
127#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 162 static const struct text_message message = { lines, 2 };
128static int close_wrapper(int fd); 163 button_clear_queue(); /* Empty the keyboard buffer */
129static int creat_wrapper(const char *pathname, mode_t mode); 164 gui_syncyesno_run(&message, NULL, NULL);
130#endif
131 165
132static void* plugin_get_audio_buffer(size_t *buffer_size); 166 FOR_EACH_BITARRAY_SET_BIT(&open_files_bitmap, fildes)
133static void plugin_release_audio_buffer(void); 167 WRAPPER(close)(fildes);
134static void plugin_tsr(bool (*exit_callback)(bool)); 168}
169
170#else /* !HAVE_PLUGIN_CHECK_OPEN_CLOSE */
171
172#define PCOC_WRAPPER(_x_) FS_PREFIX(_x_)
173#define plugin_check_open_close__enter()
174#define plugin_check_open_close__exit()
175
176#endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */
135 177
136static const struct plugin_api rockbox_api = { 178static const struct plugin_api rockbox_api = {
137 179
@@ -339,24 +381,16 @@ static const struct plugin_api rockbox_api = {
339 381
340 /* file */ 382 /* file */
341 open_utf8, 383 open_utf8,
342 (open_func)open_wrapper, 384 PCOC_WRAPPER(open),
343#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 385 PCOC_WRAPPER(creat),
344 close_wrapper, 386 PCOC_WRAPPER(close),
345#else 387 FS_PREFIX(read),
346 PREFIX(close), 388 FS_PREFIX(lseek),
347#endif 389 FS_PREFIX(write),
348 (read_func)PREFIX(read), 390 FS_PREFIX(remove),
349 PREFIX(lseek), 391 FS_PREFIX(rename),
350#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 392 FS_PREFIX(ftruncate),
351 (creat_func)creat_wrapper, 393 FS_PREFIX(filesize),
352#else
353 PREFIX(creat),
354#endif
355 (write_func)PREFIX(write),
356 PREFIX(remove),
357 PREFIX(rename),
358 PREFIX(ftruncate),
359 filesize,
360 fdprintf, 394 fdprintf,
361 read_line, 395 read_line,
362 settings_parseline, 396 settings_parseline,
@@ -369,18 +403,18 @@ static const struct plugin_api rockbox_api = {
369#endif /* USING_STORAGE_CALLBACK */ 403#endif /* USING_STORAGE_CALLBACK */
370 reload_directory, 404 reload_directory,
371 create_numbered_filename, 405 create_numbered_filename,
372 file_exists, 406 FS_PREFIX(file_exists),
373 strip_extension, 407 strip_extension,
374 crc_32, 408 crc_32,
375 filetype_get_attr, 409 filetype_get_attr,
376 410
377 /* dir */ 411 /* dir */
378 (opendir_func)opendir, 412 FS_PREFIX(opendir),
379 (closedir_func)closedir, 413 FS_PREFIX(closedir),
380 (readdir_func)readdir, 414 FS_PREFIX(readdir),
381 mkdir, 415 FS_PREFIX(mkdir),
382 rmdir, 416 FS_PREFIX(rmdir),
383 dir_exists, 417 FS_PREFIX(dir_exists),
384 dir_get_info, 418 dir_get_info,
385 419
386 /* browsing */ 420 /* browsing */
@@ -688,10 +722,11 @@ static const struct plugin_api rockbox_api = {
688#endif 722#endif
689 srand, 723 srand,
690 rand, 724 rand,
691 (qsort_func)qsort, 725 (void *)qsort,
692 kbd_input, 726 kbd_input,
693 get_time, 727 get_time,
694 set_time, 728 set_time,
729 gmtime_r,
695#if CONFIG_RTC 730#if CONFIG_RTC
696 mktime, 731 mktime,
697#endif 732#endif
@@ -891,9 +926,7 @@ int plugin_load(const char* plugin, const void* parameter)
891 /* allow voice to back off if the plugin needs lots of memory */ 926 /* allow voice to back off if the plugin needs lots of memory */
892 talk_buffer_set_policy(TALK_BUFFER_LOOSE); 927 talk_buffer_set_policy(TALK_BUFFER_LOOSE);
893 928
894#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 929 plugin_check_open_close__enter();
895 open_files = 0;
896#endif
897 930
898 int rc = p_hdr->entry_point(parameter); 931 int rc = p_hdr->entry_point(parameter);
899 932
@@ -947,24 +980,7 @@ int plugin_load(const char* plugin, const void* parameter)
947 FOR_NB_SCREENS(i) 980 FOR_NB_SCREENS(i)
948 viewportmanager_theme_undo(i, true); 981 viewportmanager_theme_undo(i, true);
949 982
950#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE 983 plugin_check_open_close__exit();
951 if(open_files != 0 && !current_plugin_handle)
952 {
953 int fd;
954 logf("Plugin '%s' leaks file handles", plugin);
955
956 static const char *lines[] =
957 { ID2P(LANG_PLUGIN_ERROR),
958 "#leak-file-handles" };
959 static const struct text_message message={ lines, 2 };
960 button_clear_queue(); /* Empty the keyboard buffer */
961 gui_syncyesno_run(&message, NULL, NULL);
962
963 for(fd=0; fd < MAX_OPEN_FILES; fd++)
964 if(open_files & (1<<fd))
965 close_wrapper(fd);
966 }
967#endif
968 984
969 if (rc == PLUGIN_ERROR) 985 if (rc == PLUGIN_ERROR)
970 splash(HZ*2, str(LANG_PLUGIN_ERROR)); 986 splash(HZ*2, str(LANG_PLUGIN_ERROR));
@@ -1027,55 +1043,3 @@ char *plugin_get_current_filename(void)
1027{ 1043{
1028 return current_plugin; 1044 return current_plugin;
1029} 1045}
1030
1031static int open_wrapper(const char* pathname, int flags, ...)
1032{
1033/* we don't have an 'open' function. it's a define. and we need
1034 * the real file_open, hence PREFIX() doesn't work here */
1035 int fd;
1036#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
1037 if (flags & O_CREAT)
1038 {
1039 va_list ap;
1040 va_start(ap, flags);
1041 fd = open(pathname, flags, va_arg(ap, unsigned int));
1042 va_end(ap);
1043 }
1044 else
1045 fd = open(pathname, flags);
1046#else
1047 fd = file_open(pathname,flags);
1048#endif
1049
1050#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
1051 if(fd >= 0)
1052 open_files |= 1<<fd;
1053#endif
1054 return fd;
1055}
1056
1057#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
1058static int close_wrapper(int fd)
1059{
1060 if((~open_files) & (1<<fd))
1061 {
1062 logf("double close from plugin");
1063 }
1064 if(fd >= 0)
1065 open_files &= (~(1<<fd));
1066
1067 return PREFIX(close)(fd);
1068}
1069
1070static int creat_wrapper(const char *pathname, mode_t mode)
1071{
1072 (void)mode;
1073
1074 int fd = PREFIX(creat)(pathname, mode);
1075
1076 if(fd >= 0)
1077 open_files |= (1<<fd);
1078
1079 return fd;
1080}
1081#endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */
diff --git a/apps/plugin.h b/apps/plugin.h
index 8b8481b6ac..e55dcf13cb 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -75,7 +75,7 @@ void* plugin_get_buffer(size_t *buffer_size);
75#include "profile.h" 75#include "profile.h"
76#endif 76#endif
77#include "misc.h" 77#include "misc.h"
78#include "filefuncs.h" 78#include "pathfuncs.h"
79#if (CONFIG_CODEC == SWCODEC) 79#if (CONFIG_CODEC == SWCODEC)
80#include "pcm_mixer.h" 80#include "pcm_mixer.h"
81#include "dsp-util.h" 81#include "dsp-util.h"
@@ -160,12 +160,12 @@ void* plugin_get_buffer(size_t *buffer_size);
160#define PLUGIN_MAGIC 0x526F634B /* RocK */ 160#define PLUGIN_MAGIC 0x526F634B /* RocK */
161 161
162/* increase this every time the api struct changes */ 162/* increase this every time the api struct changes */
163#define PLUGIN_API_VERSION 231 163#define PLUGIN_API_VERSION 232
164 164
165/* update this to latest version if a change to the api struct breaks 165/* update this to latest version if a change to the api struct breaks
166 backwards compatibility (and please take the opportunity to sort in any 166 backwards compatibility (and please take the opportunity to sort in any
167 new function which are "waiting" at the end of the function table) */ 167 new function which are "waiting" at the end of the function table) */
168#define PLUGIN_MIN_API_VERSION 231 168#define PLUGIN_MIN_API_VERSION 232
169 169
170/* plugin return codes */ 170/* plugin return codes */
171/* internal returns start at 0x100 to make exit(1..255) work */ 171/* internal returns start at 0x100 to make exit(1..255) work */
@@ -433,17 +433,17 @@ struct plugin_api {
433 433
434 /* file */ 434 /* file */
435 int (*open_utf8)(const char* pathname, int flags); 435 int (*open_utf8)(const char* pathname, int flags);
436 int (*open)(const char* pathname, int flags, ...); 436 int (*open)(const char *path, int oflag, ...);
437 int (*close)(int fd); 437 int (*creat)(const char *path, mode_t mode);
438 ssize_t (*read)(int fd, void* buf, size_t count); 438 int (*close)(int fildes);
439 off_t (*lseek)(int fd, off_t offset, int whence); 439 ssize_t (*read)(int fildes, void *buf, size_t nbyte);
440 int (*creat)(const char *pathname, mode_t mode); 440 off_t (*lseek)(int fildes, off_t offset, int whence);
441 ssize_t (*write)(int fd, const void* buf, size_t count); 441 ssize_t (*write)(int fildes, const void *buf, size_t nbyte);
442 int (*remove)(const char* pathname); 442 int (*remove)(const char *path);
443 int (*rename)(const char* path, const char* newname); 443 int (*rename)(const char *old, const char *new);
444 int (*ftruncate)(int fd, off_t length); 444 int (*ftruncate)(int fildes, off_t length);
445 off_t (*filesize)(int fd); 445 off_t (*filesize)(int fildes);
446 int (*fdprintf)(int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); 446 int (*fdprintf)(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
447 int (*read_line)(int fd, char* buffer, int buffer_size); 447 int (*read_line)(int fd, char* buffer, int buffer_size);
448 bool (*settings_parseline)(char* line, char** name, char** value); 448 bool (*settings_parseline)(char* line, char** name, char** value);
449 void (*storage_sleep)(void); 449 void (*storage_sleep)(void);
@@ -457,7 +457,7 @@ struct plugin_api {
457 char *(*create_numbered_filename)(char *buffer, const char *path, 457 char *(*create_numbered_filename)(char *buffer, const char *path,
458 const char *prefix, const char *suffix, 458 const char *prefix, const char *suffix,
459 int numberlen IF_CNFN_NUM_(, int *num)); 459 int numberlen IF_CNFN_NUM_(, int *num));
460 bool (*file_exists)(const char *file); 460 bool (*file_exists)(const char *path);
461 char* (*strip_extension)(char* buffer, int buffer_size, const char *filename); 461 char* (*strip_extension)(char* buffer, int buffer_size, const char *filename);
462 uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32); 462 uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32);
463 463
@@ -466,13 +466,13 @@ struct plugin_api {
466 466
467 467
468 /* dir */ 468 /* dir */
469 DIR* (*opendir)(const char* name); 469 DIR * (*opendir)(const char *dirname);
470 int (*closedir)(DIR* dir); 470 int (*closedir)(DIR *dirp);
471 struct dirent* (*readdir)(DIR* dir); 471 struct dirent * (*readdir)(DIR *dirp);
472 int (*mkdir)(const char *name); 472 int (*mkdir)(const char *path);
473 int (*rmdir)(const char *name); 473 int (*rmdir)(const char *path);
474 bool (*dir_exists)(const char *path); 474 bool (*dir_exists)(const char *dirname);
475 struct dirinfo (*dir_get_info)(DIR* parent, struct dirent *entry); 475 struct dirinfo (*dir_get_info)(DIR *dirp, struct dirent *entry);
476 476
477 /* browsing */ 477 /* browsing */
478 void (*browse_context_init)(struct browse_context *browse, 478 void (*browse_context_init)(struct browse_context *browse,
@@ -838,6 +838,7 @@ struct plugin_api {
838 int (*kbd_input)(char* buffer, int buflen); 838 int (*kbd_input)(char* buffer, int buflen);
839 struct tm* (*get_time)(void); 839 struct tm* (*get_time)(void);
840 int (*set_time)(const struct tm *tm); 840 int (*set_time)(const struct tm *tm);
841 struct tm * (*gmtime_r)(const time_t *timep, struct tm *tm);
841#if CONFIG_RTC 842#if CONFIG_RTC
842 time_t (*mktime)(struct tm *t); 843 time_t (*mktime)(struct tm *t);
843#endif 844#endif
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index 0f3ec5c458..3115da94a4 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -99,13 +99,12 @@ static bool file_properties(char* selected_file)
99 log = human_size_log((unsigned long)info.size); 99 log = human_size_log((unsigned long)info.size);
100 rb->snprintf(str_size, sizeof str_size, "%lu %cB", 100 rb->snprintf(str_size, sizeof str_size, "%lu %cB",
101 ((unsigned long)info.size) >> (log*10), human_size_prefix[log]); 101 ((unsigned long)info.size) >> (log*10), human_size_prefix[log]);
102 struct tm tm;
103 rb->gmtime_r(&info.mtime, &tm);
102 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", 104 rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
103 ((info.wrtdate >> 9 ) & 0x7F) + 1980, /* year */ 105 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
104 ((info.wrtdate >> 5 ) & 0x0F), /* month */ 106 rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
105 ((info.wrtdate ) & 0x1F)); /* day */ 107 tm.tm_hour, tm.tm_min, tm.tm_sec);
106 rb->snprintf(str_time, sizeof str_time, "%02d:%02d",
107 ((info.wrttime >> 11) & 0x1F), /* hour */
108 ((info.wrttime >> 5 ) & 0x3F)); /* minutes */
109 108
110 num_properties = 5; 109 num_properties = 5;
111 110
@@ -175,7 +174,10 @@ static bool _dir_properties(DPS* dps)
175 dirlen = rb->strlen(dps->dirname); 174 dirlen = rb->strlen(dps->dirname);
176 dir = rb->opendir(dps->dirname); 175 dir = rb->opendir(dps->dirname);
177 if (!dir) 176 if (!dir)
177 {
178 rb->splashf(HZ*2, "%s", dps->dirname);
178 return false; /* open error */ 179 return false; /* open error */
180 }
179 181
180 /* walk through the directory content */ 182 /* walk through the directory content */
181 while(result && (0 != (entry = rb->readdir(dir)))) 183 while(result && (0 != (entry = rb->readdir(dir))))
diff --git a/apps/radio/presets.c b/apps/radio/presets.c
index 9eab4901f1..d9a2aa9bcd 100644
--- a/apps/radio/presets.c
+++ b/apps/radio/presets.c
@@ -30,7 +30,7 @@
30#include "file.h" 30#include "file.h"
31#include "string-extra.h" 31#include "string-extra.h"
32#include "misc.h" 32#include "misc.h"
33#include "filefuncs.h" 33#include "pathfuncs.h"
34#include "lang.h" 34#include "lang.h"
35#include "action.h" 35#include "action.h"
36#include "list.h" 36#include "list.h"
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c
index 283815167a..5e1a0ad5cf 100644
--- a/apps/radio/radioart.c
+++ b/apps/radio/radioart.c
@@ -31,7 +31,7 @@
31#include "file.h" 31#include "file.h"
32#include "kernel.h" 32#include "kernel.h"
33#include "string-extra.h" 33#include "string-extra.h"
34#include "filefuncs.h" 34#include "pathfuncs.h"
35#include "core_alloc.h" 35#include "core_alloc.h"
36 36
37#define MAX_RADIOART_IMAGES 10 37#define MAX_RADIOART_IMAGES 10
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
index 4cbabbc8ce..c561e36ae2 100644
--- a/apps/recorder/albumart.c
+++ b/apps/recorder/albumart.c
@@ -27,7 +27,7 @@
27#include "buffering.h" 27#include "buffering.h"
28#include "dircache.h" 28#include "dircache.h"
29#include "misc.h" 29#include "misc.h"
30#include "filefuncs.h" 30#include "pathfuncs.h"
31#include "settings.h" 31#include "settings.h"
32#include "wps.h" 32#include "wps.h"
33 33
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 5b341fd141..1c53c8026f 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -56,7 +56,7 @@
56#include "timefuncs.h" 56#include "timefuncs.h"
57#include "debug.h" 57#include "debug.h"
58#include "misc.h" 58#include "misc.h"
59#include "filefuncs.h" 59#include "pathfuncs.h"
60#include "tree.h" 60#include "tree.h"
61#include "string.h" 61#include "string.h"
62#include "dir.h" 62#include "dir.h"
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 189b2ec35c..7ec803f585 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -146,11 +146,10 @@ static int browser(void* param)
146 int i; 146 int i;
147 for (i = 0; i < NUM_VOLUMES; i++) 147 for (i = 0; i < NUM_VOLUMES; i++)
148 { 148 {
149 char vol_string[VOL_ENUM_POS + 8]; 149 char vol_string[VOL_MAX_LEN + 1];
150 if (!volume_removable(i)) 150 if (!volume_removable(i))
151 continue; 151 continue;
152 /* VOL_NAMES contains a %d */ 152 get_volume_name(i, vol_string);
153 snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, i);
154 /* test whether we would browse the external card */ 153 /* test whether we would browse the external card */
155 if (!volume_present(i) && 154 if (!volume_present(i) &&
156 (strstr(last_folder, vol_string) 155 (strstr(last_folder, vol_string)
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index b8a95f85cb..4f3693e716 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -33,7 +33,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
33#include "core_alloc.h" 33#include "core_alloc.h"
34#include "settings.h" 34#include "settings.h"
35#include "ata_idle_notify.h" 35#include "ata_idle_notify.h"
36#include "filefuncs.h" 36#include "pathfuncs.h"
37#include "appevents.h" 37#include "appevents.h"
38 38
39#if CONFIG_RTC 39#if CONFIG_RTC
diff --git a/apps/settings.c b/apps/settings.c
index f2a923e24d..819924a421 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -822,6 +822,10 @@ void settings_apply(bool read_disk)
822#ifdef HAVE_LCD_BITMAP 822#ifdef HAVE_LCD_BITMAP
823 int rc; 823 int rc;
824#endif 824#endif
825 CHART(">set_codepage");
826 set_codepage(global_settings.default_codepage);
827 CHART("<set_codepage");
828
825 sound_settings_apply(); 829 sound_settings_apply();
826 830
827#ifdef HAVE_DISK_STORAGE 831#ifdef HAVE_DISK_STORAGE
@@ -1008,10 +1012,6 @@ void settings_apply(bool read_disk)
1008 lcd_scroll_delay(global_settings.scroll_delay); 1012 lcd_scroll_delay(global_settings.scroll_delay);
1009 1013
1010 1014
1011 CHART(">set_codepage");
1012 set_codepage(global_settings.default_codepage);
1013 CHART("<set_codepage");
1014
1015#ifdef HAVE_PLAY_FREQ 1015#ifdef HAVE_PLAY_FREQ
1016 settings_apply_play_freq(global_settings.play_frequency, false); 1016 settings_apply_play_freq(global_settings.play_frequency, false);
1017#endif 1017#endif
diff --git a/apps/settings_list.c b/apps/settings_list.c
index af83866356..53acb78d98 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1724,13 +1724,13 @@ const struct settings_list settings[] = {
1724 OFFON_SETTING(F_BANFROMQS, tagcache_autoupdate, LANG_TAGCACHE_AUTOUPDATE, false, 1724 OFFON_SETTING(F_BANFROMQS, tagcache_autoupdate, LANG_TAGCACHE_AUTOUPDATE, false,
1725 "tagcache_autoupdate", NULL), 1725 "tagcache_autoupdate", NULL),
1726#endif 1726#endif
1727 CHOICE_SETTING(0, default_codepage, LANG_DEFAULT_CODEPAGE, 0, 1727 CHOICE_SETTING(F_TEMPVAR, default_codepage, LANG_DEFAULT_CODEPAGE, 0,
1728 "default codepage", 1728 "default codepage",
1729#ifdef HAVE_LCD_BITMAP 1729#ifdef HAVE_LCD_BITMAP
1730 /* The order must match with that in unicode.c */ 1730 /* The order must match with that in unicode.c */
1731 "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256," 1731 "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256,"
1732 "iso8859-9,iso8859-2,cp1250,cp1252,sjis,gb2312,ksx1001,big5,utf-8", 1732 "iso8859-9,iso8859-2,cp1250,cp1252,sjis,gb2312,ksx1001,big5,utf-8",
1733 set_codepage, 15, 1733 NULL, 15,
1734 ID2P(LANG_CODEPAGE_LATIN1), 1734 ID2P(LANG_CODEPAGE_LATIN1),
1735 ID2P(LANG_CODEPAGE_GREEK), 1735 ID2P(LANG_CODEPAGE_GREEK),
1736 ID2P(LANG_CODEPAGE_HEBREW), ID2P(LANG_CODEPAGE_CYRILLIC), 1736 ID2P(LANG_CODEPAGE_HEBREW), ID2P(LANG_CODEPAGE_CYRILLIC),
@@ -1745,7 +1745,7 @@ const struct settings_list settings[] = {
1745#else /* !HAVE_LCD_BITMAP */ 1745#else /* !HAVE_LCD_BITMAP */
1746 /* The order must match with that in unicode.c */ 1746 /* The order must match with that in unicode.c */
1747 "iso8859-1,iso8859-7,cp1251,iso8859-9,iso8859-2,cp1250,cp1252,utf-8", 1747 "iso8859-1,iso8859-7,cp1251,iso8859-9,iso8859-2,cp1250,cp1252,utf-8",
1748 set_codepage, 8, 1748 NULL, 8,
1749 ID2P(LANG_CODEPAGE_LATIN1), ID2P(LANG_CODEPAGE_GREEK), 1749 ID2P(LANG_CODEPAGE_LATIN1), ID2P(LANG_CODEPAGE_GREEK),
1750 ID2P(LANG_CODEPAGE_CYRILLIC), ID2P(LANG_CODEPAGE_TURKISH), 1750 ID2P(LANG_CODEPAGE_CYRILLIC), ID2P(LANG_CODEPAGE_TURKISH),
1751 ID2P(LANG_CODEPAGE_LATIN_EXTENDED), 1751 ID2P(LANG_CODEPAGE_LATIN_EXTENDED),
diff --git a/apps/shortcuts.c b/apps/shortcuts.c
index a9ae8248f1..1153edd2ad 100644
--- a/apps/shortcuts.c
+++ b/apps/shortcuts.c
@@ -37,7 +37,7 @@
37#include "misc.h" 37#include "misc.h"
38#include "tree.h" 38#include "tree.h"
39#include "splash.h" 39#include "splash.h"
40#include "filefuncs.h" 40#include "pathfuncs.h"
41#include "filetypes.h" 41#include "filetypes.h"
42#include "shortcuts.h" 42#include "shortcuts.h"
43#include "onplay.h" 43#include "onplay.h"
diff --git a/apps/tagcache.c b/apps/tagcache.c
index b7d5516e81..2b6041227b 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -79,7 +79,7 @@
79#include "misc.h" 79#include "misc.h"
80#include "settings.h" 80#include "settings.h"
81#include "dir.h" 81#include "dir.h"
82#include "filefuncs.h" 82#include "pathfuncs.h"
83#include "structec.h" 83#include "structec.h"
84#include "debug.h" 84#include "debug.h"
85 85
@@ -88,6 +88,8 @@
88#include "eeprom_settings.h" 88#include "eeprom_settings.h"
89#endif 89#endif
90 90
91#undef HAVE_DIRCACHE
92
91#ifdef __PCTOOL__ 93#ifdef __PCTOOL__
92#define yield() do { } while(0) 94#define yield() do { } while(0)
93#define sim_sleep(timeout) do { } while(0) 95#define sim_sleep(timeout) do { } while(0)
@@ -2986,20 +2988,21 @@ static bool commit(void)
2986 /* Try to steal every buffer we can :) */ 2988 /* Try to steal every buffer we can :) */
2987 if (tempbuf_size == 0) 2989 if (tempbuf_size == 0)
2988 local_allocation = true; 2990 local_allocation = true;
2989 2991
2992#if 0 /* FIXME: How much big? dircache buffer can no longer be taken but
2993 may be freed to make room and the cache resumed. --jethead71 */
2990#ifdef HAVE_DIRCACHE 2994#ifdef HAVE_DIRCACHE
2991 if (tempbuf_size == 0) 2995 if (tempbuf_size == 0)
2992 { 2996 {
2993 /* Try to steal the dircache buffer. */ 2997 /* Shut down dircache to free its allocation. */
2994 tempbuf = dircache_steal_buffer(&tempbuf_size); 2998 dircache_free_buffer();
2995 tempbuf_size &= ~0x03;
2996
2997 if (tempbuf_size > 0) 2999 if (tempbuf_size > 0)
2998 { 3000 {
2999 dircache_buffer_stolen = true; 3001 dircache_buffer_stolen = true;
3000 } 3002 }
3001 } 3003 }
3002#endif 3004#endif
3005#endif
3003 3006
3004#ifdef HAVE_TC_RAMCACHE 3007#ifdef HAVE_TC_RAMCACHE
3005 if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0) 3008 if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0)
@@ -4462,7 +4465,7 @@ static bool check_dir(const char *dirname, int add_files)
4462 tc_stat.curentry = curpath; 4465 tc_stat.curentry = curpath;
4463 4466
4464 /* Add a new entry to the temporary db file. */ 4467 /* Add a new entry to the temporary db file. */
4465 add_tagcache(curpath, (info.wrtdate << 16) | info.wrttime 4468 add_tagcache(curpath, info.mtime
4466#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 4469#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
4467 , dir->internal_entry 4470 , dir->internal_entry
4468#endif 4471#endif
@@ -4780,7 +4783,7 @@ void tagcache_shutdown(void)
4780 /* Flush the command queue. */ 4783 /* Flush the command queue. */
4781 run_command_queue(true); 4784 run_command_queue(true);
4782 4785
4783#ifdef HAVE_EEPROM_SETTINGS 4786#if defined(HAVE_EEPROM_SETTINGS) && defined(HAVE_TC_RAMCACHE)
4784 if (tc_stat.ramcache) 4787 if (tc_stat.ramcache)
4785 tagcache_dumpsave(); 4788 tagcache_dumpsave();
4786#endif 4789#endif
diff --git a/apps/tree.c b/apps/tree.c
index f72774fe1e..938e44d350 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -52,7 +52,7 @@
52#include "talk.h" 52#include "talk.h"
53#include "filetypes.h" 53#include "filetypes.h"
54#include "misc.h" 54#include "misc.h"
55#include "filefuncs.h" 55#include "pathfuncs.h"
56#include "filetree.h" 56#include "filetree.h"
57#include "tagtree.h" 57#include "tagtree.h"
58#ifdef HAVE_RECORDING 58#ifdef HAVE_RECORDING
@@ -1205,26 +1205,36 @@ void tree_flush(void)
1205#endif 1205#endif
1206 1206
1207#ifdef HAVE_DIRCACHE 1207#ifdef HAVE_DIRCACHE
1208 int old_val = global_status.dircache_size;
1209#ifdef HAVE_EEPROM_SETTINGS
1210 bool savecache = false;
1211#endif
1212
1213 if (global_settings.dircache)
1208 { 1214 {
1209 int old_val = global_status.dircache_size; 1215 dircache_suspend();
1210 if (global_settings.dircache) 1216
1211 { 1217 struct dircache_info info;
1212 if (!dircache_is_initializing()) 1218 dircache_get_info(&info);
1213 global_status.dircache_size = dircache_get_cache_size(); 1219
1214# ifdef HAVE_EEPROM_SETTINGS 1220 global_status.dircache_size = info.last_size;
1215 if (firmware_settings.initialized) 1221 #ifdef HAVE_EEPROM_SETTINGS
1216 dircache_save(); 1222 savecache = firmware_settings.initialized;
1217# endif 1223 #endif
1218 dircache_suspend();
1219 }
1220 else
1221 {
1222 global_status.dircache_size = 0;
1223 }
1224 if (old_val != global_status.dircache_size)
1225 status_save();
1226 } 1224 }
1227#endif 1225 else
1226 {
1227 global_status.dircache_size = 0;
1228 }
1229
1230 if (old_val != global_status.dircache_size)
1231 status_save();
1232
1233 #ifdef HAVE_EEPROM_SETTINGS
1234 if (savecache)
1235 dircache_save();
1236 #endif
1237#endif /* HAVE_DIRCACHE */
1228} 1238}
1229 1239
1230void tree_restore(void) 1240void tree_restore(void)
@@ -1238,15 +1248,14 @@ void tree_restore(void)
1238#endif 1248#endif
1239 1249
1240#ifdef HAVE_DIRCACHE 1250#ifdef HAVE_DIRCACHE
1241 remove(DIRCACHE_FILE); 1251 if (global_settings.dircache && dircache_resume() > 0)
1242 if (global_settings.dircache)
1243 { 1252 {
1244 /* Print "Scanning disk..." to the display. */ 1253 /* Print "Scanning disk..." to the display. */
1245 splash(0, str(LANG_SCANNING_DISK)); 1254 splash(0, str(LANG_SCANNING_DISK));
1246 1255 dircache_wait();
1247 dircache_build(global_status.dircache_size);
1248 } 1256 }
1249#endif 1257#endif
1258
1250#ifdef HAVE_TAGCACHE 1259#ifdef HAVE_TAGCACHE
1251 tagcache_start_scan(); 1260 tagcache_start_scan();
1252#endif 1261#endif