diff options
Diffstat (limited to 'apps/onplay.c')
-rw-r--r-- | apps/onplay.c | 844 |
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 @@ #include "statusbar-skinned.h" #include "pitchscreen.h" #include "viewport.h" -#include "filefuncs.h" +#include "pathfuncs.h" #include "shortcuts.h" static int context; -static const char* selected_file = NULL; +static const char *selected_file = NULL; static int selected_file_attr = 0; static int onplay_result = ONPLAY_OK; -static char clipboard_selection[MAX_PATH]; -static int clipboard_selection_attr = 0; -static bool clipboard_is_copy = false; /* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */ #define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \ @@ -82,6 +79,63 @@ static bool clipboard_is_copy = false; MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ { (void*)name##_},{.callback_and_desc = & name##__}}; +/* Used for directory move, copy and delete */ +struct dirrecurse_params +{ + char path[MAX_PATH]; /* Buffer for full path */ + size_t append; /* Append position in 'path' for stack push */ +}; + +enum clipboard_op_flags +{ + PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */ + PASTE_COPY = 0x01, /* Is a copy operation */ + PASTE_OVERWRITE = 0x02, /* Overwrite destination */ + PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */ +}; + +/* result codec of various onplay operations */ +enum onplay_result_code +{ + /* Anything < 0 is failure */ + OPRC_SUCCESS = 0, /* All operations completed successfully */ + OPRC_NOOP = 1, /* Operation didn't need to do anything */ + OPRC_CANCELLED = 2, /* Operation was cancelled by user */ + OPRC_NOOVERWRT = 3, +}; + +static struct clipboard +{ + char path[MAX_PATH]; /* Clipped file's path */ + unsigned int attr; /* Clipped file's attributes */ + unsigned int flags; /* Operation type flags */ +} clipboard; + +/* Empty the clipboard */ +static void clipboard_clear_selection(struct clipboard *clip) +{ + clip->path[0] = '\0'; + clip->attr = 0; + clip->flags = 0; +} + +/* Store the selection in the clipboard */ +static bool clipboard_clip(struct clipboard *clip, const char *path, + unsigned int attr, unsigned int flags) +{ + /* if it fits it clips */ + if (strlcpy(clip->path, path, sizeof (clip->path)) + < sizeof (clip->path)) { + clip->attr = attr; + clip->flags = flags; + return true; + } + else { + clipboard_clear_selection(clip); + return false; + } +} + /* ----------------------------------------------------------------------- */ /* Displays the bookmark menu options for the user to decide. This is an */ /* interface function. */ @@ -492,438 +546,578 @@ static void draw_slider(void) #define draw_slider() #endif -/* helper function to remove a non-empty directory */ -static int remove_dir(char* dirname, int len) +static void clear_display(bool update) { - int result = 0; - DIR* dir; - int dirlen = strlen(dirname); + struct viewport vp; - dir = opendir(dirname); - if (!dir) + FOR_NB_SCREENS(i) + { + struct screen * screen = &screens[i]; + viewport_set_defaults(&vp, screen->screen_type); + screen->set_viewport(&vp); + screen->clear_viewport(); + if (update) { + screen->update_viewport(); + } + screen->set_viewport(NULL); + } +} + +static void splash_path(const char *path) +{ + clear_display(false); + path_basename(path, &path); + splash(0, path); + draw_slider(); +} + +/* Splashes the path and checks the keys */ +static bool poll_cancel_action(const char *path) +{ + splash_path(path); + return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); +} + +static int confirm_overwrite(void) +{ + static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) }; + static const struct text_message message = { lines, 1 }; + return gui_syncyesno_run(&message, NULL, NULL); +} + +static int confirm_delete(const char *file) +{ + const char *lines[] = { ID2P(LANG_REALLY_DELETE), file }; + const char *yes_lines[] = { ID2P(LANG_DELETING), file }; + const struct text_message message = { lines, 2 }; + const struct text_message yes_message = { yes_lines, 2 }; + return gui_syncyesno_run(&message, &yes_message, NULL); +} + +static bool check_new_name(const char *basename) +{ + /* at least prevent escapes out of the base directory from keyboard- + entered filenames; the file code should reject other invalidities */ + return *basename != '\0' && !strchr(basename, PATH_SEPCH) && + !is_dotdir_name(basename); +} + +static void splash_cancelled(void) +{ + clear_display(true); + splash(HZ, ID2P(LANG_CANCEL)); +} + +static void splash_failed(int lang_what) +{ + cond_talk_ids_fq(lang_what, LANG_FAILED); + clear_display(true); + splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED)); +} + +/* helper function to remove a non-empty directory */ +static int remove_dir(struct dirrecurse_params *parm) +{ + DIR *dir = opendir(parm->path); + if (!dir) { return -1; /* open error */ + } - while(true) - { - struct dirent* entry; - /* walk through the directory content */ - entry = readdir(dir); - if (!entry) + size_t append = parm->append; + int rc = OPRC_SUCCESS; + + /* walk through the directory content */ + while (rc == OPRC_SUCCESS) { + errno = 0; /* distinguish failure from eod */ + struct dirent *entry = readdir(dir); + if (!entry) { + if (errno) { + rc = -1; + } break; + } + struct dirinfo info = dir_get_info(dir, entry); - dirname[dirlen] ='\0'; - /* inform the user which dir we're deleting */ - splash(0, dirname); + if ((info.attribute & ATTR_DIRECTORY) && + is_dotdir_name(entry->d_name)) { + continue; /* skip these */ + } /* append name to current directory */ - snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); - if (info.attribute & ATTR_DIRECTORY) - { /* remove a subdirectory */ - if (!strcmp((char *)entry->d_name, ".") || - !strcmp((char *)entry->d_name, "..")) - continue; /* skip these */ - - result = remove_dir(dirname, len); /* recursion */ - if (result) - break; /* or better continue, delete what we can? */ - } - else - { /* remove a file */ - draw_slider(); - result = remove(dirname); + parm->append = append + path_append(&parm->path[append], + PA_SEP_HARD, entry->d_name, + sizeof (parm->path) - append); + if (parm->append >= sizeof (parm->path)) { + rc = -1; + break; /* no space left in buffer */ } - if(ACTION_STD_CANCEL == get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) - { - splash(HZ, ID2P(LANG_CANCEL)); - result = -1; - break; + + if (info.attribute & ATTR_DIRECTORY) { + /* remove a subdirectory */ + rc = remove_dir(parm); + } else { + /* remove a file */ + if (poll_cancel_action(parm->path)) { + rc = OPRC_CANCELLED; + break; + } + + rc = remove(parm->path); } + + /* Remove basename we added above */ + parm->path[append] = '\0'; } - closedir(dir); - if (!result) - { /* remove the now empty directory */ - dirname[dirlen] = '\0'; /* terminate to original length */ + closedir(dir); - result = rmdir(dirname); + if (rc == 0) { + /* remove the now empty directory */ + if (poll_cancel_action(parm->path)) { + rc = OPRC_CANCELLED; + } else { + rc = rmdir(parm->path); + } } - return result; + return rc; } - /* share code for file and directory deletion, saves space */ -static bool delete_file_dir(void) +static int delete_file_dir(void) { - char file_to_delete[MAX_PATH]; - strcpy(file_to_delete, selected_file); + if (confirm_delete(selected_file) != YESNO_YES) { + return 1; + } - const char *lines[]={ - ID2P(LANG_REALLY_DELETE), - file_to_delete - }; - const char *yes_lines[]={ - ID2P(LANG_DELETING), - file_to_delete - }; + clear_display(true); + splash(HZ/2, str(LANG_DELETING)); - const struct text_message message={lines, 2}; - const struct text_message yes_message={yes_lines, 2}; + int rc = -1; - if(gui_syncyesno_run(&message, &yes_message, NULL)!=YESNO_YES) - return false; + if (selected_file_attr & ATTR_DIRECTORY) { /* true if directory */ + struct dirrecurse_params parm; + parm.append = strlcpy(parm.path, selected_file, sizeof (parm.path)); - splash(0, str(LANG_DELETING)); + if (parm.append < sizeof (parm.path)) { + cpu_boost(true); + rc = remove_dir(&parm); + cpu_boost(false); + } + } else { + rc = remove(selected_file); + } - int res; - if (selected_file_attr & ATTR_DIRECTORY) /* true if directory */ - { - char pathname[MAX_PATH]; /* space to go deep */ - cpu_boost(true); - strlcpy(pathname, file_to_delete, sizeof(pathname)); - res = remove_dir(pathname, sizeof(pathname)); - cpu_boost(false); + if (rc < OPRC_SUCCESS) { + splash_failed(LANG_DELETE); + } else if (rc == OPRC_CANCELLED) { + splash_cancelled(); } - else - res = remove(file_to_delete); - if (!res) + if (rc != OPRC_NOOP) { + /* Could have failed after some but not all needed changes; reload */ onplay_result = ONPLAY_RELOAD_DIR; + } - return (res == 0); + return 1; } -static bool rename_file(void) +static int rename_file(void) { + int rc = -1; char newname[MAX_PATH]; - char* ptr = strrchr(selected_file, '/') + 1; - int pathlen = (ptr - selected_file); - strlcpy(newname, selected_file, sizeof(newname)); - if (!kbd_input(newname + pathlen, (sizeof newname)-pathlen)) { - if (!strlen(newname + pathlen) || - (rename(selected_file, newname) < 0)) { - cond_talk_ids_fq(LANG_RENAME, LANG_FAILED); - splashf(HZ*2, "%s %s", str(LANG_RENAME), str(LANG_FAILED)); + const char *oldbase, *selection = selected_file; + + path_basename(selection, &oldbase); + size_t pathlen = oldbase - selection; + char *newbase = newname + pathlen; + + if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) { + /* Too long */ + } else if (kbd_input(newbase, sizeof (newname) - pathlen) < 0) { + rc = OPRC_CANCELLED; + } else if (!strcmp(oldbase, newbase)) { + rc = OPRC_NOOP; /* No change at all */ + } else if (check_new_name(newbase)) { + switch (relate(selection, newname)) + { + case RELATE_DIFFERENT: + if (file_exists(newname)) { + break; /* don't overwrite */ + } + /* Fall-through */ + case RELATE_SAME: + rc = rename(selection, newname); + break; + case RELATE_PREFIX: + default: + break; } - else - onplay_result = ONPLAY_RELOAD_DIR; } - return false; + if (rc < OPRC_SUCCESS) { + splash_failed(LANG_RENAME); + } else if (rc == OPRC_CANCELLED) { + /* splash_cancelled(); kbd_input() splashes it */ + } else if (rc == OPRC_SUCCESS) { + onplay_result = ONPLAY_RELOAD_DIR; + } + + return 1; } -static bool create_dir(void) +static int create_dir(void) { + int rc = -1; char dirname[MAX_PATH]; - char *cwd; - int rc; - int pathlen; - - cwd = getcwd(NULL, 0); - memset(dirname, 0, sizeof dirname); - - snprintf(dirname, sizeof dirname, "%s/", cwd[1] ? cwd : ""); - - pathlen = strlen(dirname); - rc = kbd_input(dirname + pathlen, (sizeof dirname)-pathlen); - if (rc < 0) - return false; + size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, + sizeof (dirname)); + char *basename = dirname + pathlen; + + if (pathlen >= sizeof (dirname)) { + /* Too long */ + } else if (kbd_input(basename, sizeof (dirname) - pathlen) < 0) { + rc = OPRC_CANCELLED; + } else if (check_new_name(basename)) { + rc = mkdir(dirname); + } - rc = mkdir(dirname); - if (rc < 0) { - cond_talk_ids_fq(LANG_CREATE_DIR, LANG_FAILED); - splashf(HZ, (unsigned char *)"%s %s", str(LANG_CREATE_DIR), - str(LANG_FAILED)); - } else { + if (rc < OPRC_SUCCESS) { + splash_failed(LANG_CREATE_DIR); + } else if (rc == OPRC_CANCELLED) { + /* splash_cancelled(); kbd_input() splashes it */ + } else if (rc == OPRC_SUCCESS) { onplay_result = ONPLAY_RELOAD_DIR; } - return true; + return 1; } -/* Store the current selection in the clipboard */ -static bool clipboard_clip(bool copy) +/* Paste a file */ +static int clipboard_pastefile(const char *src, const char *target, + unsigned int flags) { - clipboard_selection[0] = 0; - strlcpy(clipboard_selection, selected_file, sizeof(clipboard_selection)); - clipboard_selection_attr = selected_file_attr; - clipboard_is_copy = copy; - - return true; -} + int rc = -1; + + while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { + if ((flags & PASTE_OVERWRITE) || !file_exists(target)) { + /* Rename and possibly overwrite the file */ + if (poll_cancel_action(src)) { + rc = OPRC_CANCELLED; + } else { + rc = rename(src, target); + } -static bool clipboard_cut(void) -{ - return clipboard_clip(false); -} + #ifdef HAVE_MULTIVOLUME + if (rc < 0 && errno == EXDEV) { + /* Failed because cross volume rename doesn't work; force + a move instead */ + flags |= PASTE_EXDEV; + break; + } + #endif /* HAVE_MULTIVOLUME */ + } -static bool clipboard_copy(void) -{ - return clipboard_clip(true); -} + return rc; + } -/* Paste a file to a new directory. Will overwrite always. */ -static bool clipboard_pastefile(const char *src, const char *target, bool copy) -{ - int src_fd, target_fd; + /* See if we can get the plugin buffer for the file copy buffer */ size_t buffersize; - ssize_t size, bytesread, byteswritten; - char *buffer; - bool result = false; - - if (copy) { - /* See if we can get the plugin buffer for the file copy buffer */ - buffer = (char *) plugin_get_buffer(&buffersize); - if (buffer == NULL || buffersize < 512) { - /* Not large enough, try for a disk sector worth of stack - instead */ - buffersize = 512; - buffer = (char *) __builtin_alloca(buffersize); - } + char *buffer = (char *) plugin_get_buffer(&buffersize); + if (buffer == NULL || buffersize < 512) { + /* Not large enough, try for a disk sector worth of stack + instead */ + buffersize = 512; + buffer = (char *)alloca(buffersize); + } - if (buffer == NULL) { - return false; - } + if (buffer == NULL) { + return -1; + } - buffersize &= ~0x1ff; /* Round buffer size to multiple of sector - size */ + buffersize &= ~0x1ff; /* Round buffer size to multiple of sector + size */ - src_fd = open(src, O_RDONLY); + int src_fd = open(src, O_RDONLY); + if (src_fd >= 0) { + int oflag = O_WRONLY|O_CREAT; - if (src_fd >= 0) { - target_fd = creat(target, 0666); + if (!(flags & PASTE_OVERWRITE)) { + oflag |= O_EXCL; + } - if (target_fd >= 0) { - result = true; + int target_fd = open(target, oflag, 0666); + if (target_fd >= 0) { + off_t total_size = 0; + off_t next_cancel_test = 0; /* No excessive button polling */ - size = filesize(src_fd); + rc = OPRC_SUCCESS; - if (size == -1) { - result = false; + while (rc == OPRC_SUCCESS) { + if (total_size >= next_cancel_test) { + next_cancel_test = total_size + 0x10000; + if (poll_cancel_action(src)) { + rc = OPRC_CANCELLED; + break; + } } - while(size > 0) { - bytesread = read(src_fd, buffer, buffersize); - - if (bytesread == -1) { - result = false; - break; + ssize_t bytesread = read(src_fd, buffer, buffersize); + if (bytesread <= 0) { + if (bytesread < 0) { + rc = -1; } + /* else eof on buffer boundary; nothing to write */ + break; + } - size -= bytesread; - - while(bytesread > 0) { - byteswritten = write(target_fd, buffer, bytesread); - - if (byteswritten < 0) { - result = false; - size = 0; - break; - } - - bytesread -= byteswritten; - draw_slider(); - } + ssize_t byteswritten = write(target_fd, buffer, bytesread); + if (byteswritten < bytesread) { + /* Some I/O error */ + rc = -1; + break; } - close(target_fd); + total_size += byteswritten; - /* Copy failed. Cleanup. */ - if (!result) { - remove(target); + if (bytesread < (ssize_t)buffersize) { + /* EOF with trailing bytes */ + break; } } - close(src_fd); - } - } else { - result = rename(src, target) == 0; -#ifdef HAVE_MULTIVOLUME - if (!result) { - if (errno == EXDEV) { - /* Failed because cross volume rename doesn't work. Copy - instead */ - result = clipboard_pastefile(src, target, true); - - if (result) { - result = remove(src) == 0; - } + if (rc == OPRC_SUCCESS) { + /* If overwriting, set the correct length if original was + longer */ + rc = ftruncate(target_fd, total_size); + } + + close(target_fd); + + if (rc != OPRC_SUCCESS) { + /* Copy failed. Cleanup. */ + remove(target); } } -#endif + + close(src_fd); } - return result; + if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { + /* Remove the source file */ + rc = remove(src); + } + + return rc; } -/* Paste a directory to a new location. Designed to be called by - clipboard_paste */ -static bool clipboard_pastedirectory(char *src, int srclen, char *target, - int targetlen, bool copy) +/* Paste a directory */ +static int clipboard_pastedirectory(struct dirrecurse_params *src, + struct dirrecurse_params *target, + unsigned int flags) { - DIR *srcdir; - int srcdirlen = strlen(src); - int targetdirlen = strlen(target); - bool result = true; - - if (!file_exists(target)) { - if (!copy) { - /* Just move the directory */ - result = rename(src, target) == 0; + int rc = -1; + + while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { + if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) { + /* Just try to move the directory */ + if (poll_cancel_action(src->path)) { + rc = OPRC_CANCELLED; + } else { + rc = rename(src->path, target->path); + } -#ifdef HAVE_MULTIVOLUME - if (!result && errno == EXDEV) { - /* Try a copy as we're going across devices */ - result = clipboard_pastedirectory(src, srclen, target, - targetlen, true); - - /* If it worked, remove the source directory */ - if (result) { - remove_dir(src, srclen); + if (rc < 0) { + int errnum = errno; + if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { + /* Directory is not empty thus rename() will not do a quick + overwrite */ + break; } + #ifdef HAVE_MULTIVOLUME + else if (errnum == EXDEV) { + /* Failed because cross volume rename doesn't work; force + a move instead */ + flags |= PASTE_EXDEV; + break; + } + #endif /* HAVE_MULTIVOLUME */ } -#endif - return result; - } else { - /* Make a directory to copy things to */ - result = mkdir(target) == 0; } - } - /* Check if something went wrong already */ - if (!result) { - return result; + return rc; } - srcdir = opendir(src); - if (!srcdir) { - return false; + DIR *srcdir = opendir(src->path); + + if (srcdir) { + /* Make a directory to copy things to */ + rc = mkdir(target->path); + if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { + /* Exists and overwrite was approved */ + rc = OPRC_SUCCESS; + } } - /* This loop will exit as soon as there's a problem */ - while(result) - { - struct dirent* entry; - /* walk through the directory content */ - entry = readdir(srcdir); - if (!entry) + size_t srcap = src->append, targetap = target->append; + + /* Walk through the directory content; this loop will exit as soon as + there's a problem */ + while (rc == OPRC_SUCCESS) { + errno = 0; /* Distinguish failure from eod */ + struct dirent *entry = readdir(srcdir); + if (!entry) { + if (errno) { + rc = -1; + } break; + } struct dirinfo info = dir_get_info(srcdir, entry); - /* append name to current directory */ - snprintf(src+srcdirlen, srclen-srcdirlen, "/%s", entry->d_name); - snprintf(target+targetdirlen, targetlen-targetdirlen, "/%s", - entry->d_name); + if ((info.attribute & ATTR_DIRECTORY) && + is_dotdir_name(entry->d_name)) { + continue; /* Skip these */ + } - DEBUGF("Copy %s to %s\n", src, target); + /* Append names to current directories */ + src->append = srcap + + path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, + sizeof(src->path) - srcap); - if (info.attribute & ATTR_DIRECTORY) - { /* copy/move a subdirectory */ - if (!strcmp((char *)entry->d_name, ".") || - !strcmp((char *)entry->d_name, "..")) - continue; /* skip these */ + target->append = targetap + + path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name, + sizeof (target->path) - targetap); - result = clipboard_pastedirectory(src, srclen, target, targetlen, - copy); /* recursion */ + if (src->append >= sizeof (src->path) || + target->append >= sizeof (target->path)) { + rc = -1; /* No space left in buffer */ + break; } - else - { /* copy/move a file */ - draw_slider(); - result = clipboard_pastefile(src, target, copy); + + if (poll_cancel_action(src->path)) { + rc = OPRC_CANCELLED; + break; + } + + DEBUGF("Copy %s to %s\n", src->path, target->path); + + if (info.attribute & ATTR_DIRECTORY) { + /* Copy/move a subdirectory */ + rc = clipboard_pastedirectory(src, target, flags); /* recursion */ + } else { + /* Copy/move a file */ + rc = clipboard_pastefile(src->path, target->path, flags); } + + /* Remove basenames we added above */ + src->path[srcap] = target->path[targetap] = '\0'; + } + + if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { + /* Remove the now empty directory */ + rc = rmdir(src->path); } closedir(srcdir); + return rc; +} - if (result) { - src[srcdirlen] = '\0'; /* terminate to original length */ - target[targetdirlen] = '\0'; /* terminate to original length */ - } +static bool clipboard_cut(void) +{ + return clipboard_clip(&clipboard, selected_file, selected_file_attr, + PASTE_CUT); +} - return result; +static bool clipboard_copy(void) +{ + return clipboard_clip(&clipboard, selected_file, selected_file_attr, + PASTE_COPY); } /* Paste the clipboard to the current directory */ -static bool clipboard_paste(void) +static int clipboard_paste(void) { - char target[MAX_PATH]; - char *cwd, *nameptr; - bool success; + if (!clipboard.path[0]) + return 1; - static const char *lines[]={ID2P(LANG_REALLY_OVERWRITE)}; - static const struct text_message message={lines, 1}; + int rc = -1; - /* Get the name of the current directory */ - cwd = getcwd(NULL, 0); + struct dirrecurse_params src, target; + unsigned int flags = clipboard.flags; /* Figure out the name of the selection */ - nameptr = strrchr(clipboard_selection, '/'); + const char *nameptr; + path_basename(clipboard.path, &nameptr); /* Final target is current directory plus name of selection */ - snprintf(target, sizeof(target), "%s%s", cwd[1] ? cwd : "", nameptr); - - /* If the target existed but they choose not to overwite, exit */ - if (file_exists(target) && - (gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO)) { - return false; - } + target.append = path_append(target.path, getcwd(NULL, 0), + nameptr, sizeof (target.path)); - if (clipboard_is_copy) { - splash(0, ID2P(LANG_COPYING)); - } - else + switch (target.append < sizeof (target.path) ? + relate(clipboard.path, target.path) : -1) { - splash(0, ID2P(LANG_MOVING)); - } + case RELATE_SAME: + rc = OPRC_NOOP; + break; + + case RELATE_DIFFERENT: + if (file_exists(target.path)) { + /* If user chooses not to overwrite, cancel */ + if (confirm_overwrite() == YESNO_NO) { + rc = OPRC_NOOVERWRT; + break; + } - /* Now figure out what we're doing */ - cpu_boost(true); - if (clipboard_selection_attr & ATTR_DIRECTORY) { - /* Recursion. Set up external stack */ - char srcpath[MAX_PATH]; - char targetpath[MAX_PATH]; - if (!strncmp(clipboard_selection, target, strlen(clipboard_selection))) - { - /* Do not allow the user to paste a directory into a dir they are - copying */ - success = 0; + flags |= PASTE_OVERWRITE; } - else - { - strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); - strlcpy(targetpath, target, sizeof(targetpath)); - success = clipboard_pastedirectory(srcpath, sizeof(srcpath), - target, sizeof(targetpath), clipboard_is_copy); + clear_display(true); + splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) : + ID2P(LANG_MOVING)); - if (success && !clipboard_is_copy) - { - strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); - remove_dir(srcpath, sizeof(srcpath)); + /* Now figure out what we're doing */ + cpu_boost(true); + + if (clipboard.attr & ATTR_DIRECTORY) { + /* Copy or move a subdirectory */ + src.append = strlcpy(src.path, clipboard.path, + sizeof (src.path)); + if (src.append < sizeof (src.path)) { + rc = clipboard_pastedirectory(&src, &target, flags); } + } else { + /* Copy or move a file */ + rc = clipboard_pastefile(clipboard.path, target.path, flags); } - } else { - success = clipboard_pastefile(clipboard_selection, target, - clipboard_is_copy); + + cpu_boost(false); + break; + + case RELATE_PREFIX: + default: /* Some other relation / failure */ + break; } - cpu_boost(false); - /* Did it work? */ - if (success) { - /* Reset everything */ - clipboard_selection[0] = 0; - clipboard_selection_attr = 0; - clipboard_is_copy = false; + clear_display(true); - /* Force reload of the current directory */ + switch (rc) + { + case OPRC_CANCELLED: + splash_cancelled(); + case OPRC_SUCCESS: onplay_result = ONPLAY_RELOAD_DIR; - } else { - cond_talk_ids_fq(LANG_PASTE, LANG_FAILED); - splashf(HZ, (unsigned char *)"%s %s", str(LANG_PASTE), - str(LANG_FAILED)); + case OPRC_NOOP: + clipboard_clear_selection(&clipboard); + case OPRC_NOOVERWRT: + break; + default: + if (rc < OPRC_SUCCESS) { + splash_failed(LANG_PASTE); + onplay_result = ONPLAY_RELOAD_DIR; + } } - return true; + return 1; } #ifdef HAVE_TAGCACHE @@ -1094,15 +1288,12 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item) { case ACTION_REQUEST_MENUITEM: #ifdef HAVE_MULTIVOLUME - if ((selected_file_attr & FAT_ATTR_VOLUME) && - (this_item == &rename_file_item || - this_item == &delete_dir_item || - this_item == &clipboard_cut_item) ) - return ACTION_EXIT_MENUITEM; /* no rename+delete for volumes */ if ((selected_file_attr & ATTR_VOLUME) && - (this_item == &delete_file_item || - this_item == &list_viewers_item)) + (this_item == &rename_file_item || + this_item == &delete_dir_item || + this_item == &clipboard_cut_item || + this_item == &list_viewers_item)) return ACTION_EXIT_MENUITEM; #endif #ifdef HAVE_TAGCACHE @@ -1117,7 +1308,7 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item) #endif if (this_item == &clipboard_paste_item) { /* visible if there is something to paste */ - return (clipboard_selection[0] != 0) ? + return (clipboard.path[0] != 0) ? action : ACTION_EXIT_MENUITEM; } else if (this_item == &create_dir_item) @@ -1232,8 +1423,7 @@ static bool delete_item(void) { #ifdef HAVE_MULTIVOLUME /* no delete for volumes */ - if ((selected_file_attr & FAT_ATTR_VOLUME) || - (selected_file_attr & ATTR_VOLUME)) + if (selected_file_attr & ATTR_VOLUME) return false; #endif return delete_file_dir(); |