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 @@
#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();