summaryrefslogtreecommitdiff
path: root/apps/onplay.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/onplay.c')
-rw-r--r--apps/onplay.c844
1 files changed, 517 insertions, 327 deletions
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();