diff options
author | William Wilgus <wilgus.william@gmail.com> | 2024-06-21 10:38:50 -0400 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2024-06-30 02:09:40 -0400 |
commit | dc7486c7de3018b78fcfeafe7a1cc5c9da168494 (patch) | |
tree | ca5326e18d88f650e8132c2b9760199701448df2 /apps/fileop.c | |
parent | c87c09658a7d65249affe3e8a814bd278998bb42 (diff) | |
download | rockbox-dc7486c7de3018b78fcfeafe7a1cc5c9da168494.tar.gz rockbox-dc7486c7de3018b78fcfeafe7a1cc5c9da168494.zip |
[Feature] onplay.c show file progress
first some clean-up of onplay.c
extend/move fileobject copy, move, delete routines in prep for other users
add error checking, better error codes
pre scan to make sure the operation doesn't exceed system resources
show progress for file and directory copies
Change-Id: Ife2e62df554892dab651bab40433bf70b27e73ff
Diffstat (limited to 'apps/fileop.c')
-rw-r--r-- | apps/fileop.c | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/apps/fileop.c b/apps/fileop.c new file mode 100644 index 0000000000..0d2dc774b9 --- /dev/null +++ b/apps/fileop.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
11 | * Copyright (C) 2024 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include <stdbool.h> | ||
23 | #include <errno.h> | ||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include "string-extra.h" | ||
28 | #include "debug.h" | ||
29 | |||
30 | #include "misc.h" | ||
31 | #include "plugin.h" | ||
32 | #include "dir.h" | ||
33 | #include "tree.h" | ||
34 | #include "fileop.h" | ||
35 | #include "pathfuncs.h" | ||
36 | |||
37 | #include "settings.h" | ||
38 | #include "lang.h" | ||
39 | #include "yesno.h" | ||
40 | #include "splash.h" | ||
41 | #include "keyboard.h" | ||
42 | |||
43 | /* Used for directory move, copy and delete */ | ||
44 | struct file_op_params | ||
45 | { | ||
46 | char path[MAX_PATH]; /* Buffer for full path */ | ||
47 | bool is_dir; | ||
48 | int objects; /* how many files and subdirectories*/ | ||
49 | int processed; | ||
50 | size_t append; /* Append position in 'path' for stack push */ | ||
51 | }; | ||
52 | |||
53 | static bool poll_cancel_action(const char *path, int operation, int current, int total) | ||
54 | { | ||
55 | const char *op_str = ""; | ||
56 | |||
57 | clear_screen_buffer(false); | ||
58 | |||
59 | if (operation == FOC_COPY) | ||
60 | op_str = str(LANG_COPYING); | ||
61 | else if (operation == FOC_MOVE) | ||
62 | op_str = str(LANG_MOVING); | ||
63 | else if (operation == FOC_COUNT) | ||
64 | op_str = str(LANG_SCANNING_DISK); | ||
65 | else if (operation == FOC_DELETE) | ||
66 | op_str = str(LANG_DELETING); | ||
67 | |||
68 | path_basename(path, &path); | ||
69 | |||
70 | if (total <= 0) | ||
71 | splashf(0, "%s (%d) %s", op_str, current, path); | ||
72 | else | ||
73 | splash_progress(current, total, "%s %s", op_str, path); | ||
74 | |||
75 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); | ||
76 | } | ||
77 | |||
78 | static struct file_op_params* init_file_op(struct file_op_params *param, | ||
79 | const char *basename, | ||
80 | const char *selected_file) | ||
81 | { | ||
82 | /* Assumes: basename will never be NULL */ | ||
83 | if (selected_file == NULL) | ||
84 | { | ||
85 | param->append = strlcpy(param->path, basename, sizeof (param->path)); | ||
86 | } | ||
87 | else | ||
88 | param->append = path_append(param->path, basename, | ||
89 | selected_file, sizeof (param->path)); | ||
90 | param->is_dir = dir_exists(param->path); | ||
91 | param->objects = 0; /* how many files and subdirectories*/ | ||
92 | param->processed = 0; | ||
93 | |||
94 | return param; | ||
95 | } | ||
96 | |||
97 | /* counts file objects, deletes file objects */ | ||
98 | static int directory_fileop(struct file_op_params *param, enum file_op_current fileop) | ||
99 | { | ||
100 | errno = 0; | ||
101 | DIR *dir = opendir(param->path); | ||
102 | if (!dir) { | ||
103 | if (errno == EMFILE) { | ||
104 | return FORC_TOO_MANY_SUBDIRS; | ||
105 | } | ||
106 | return FORC_PATH_NOT_EXIST; /* open error */ | ||
107 | } | ||
108 | int rc = FORC_SUCCESS; | ||
109 | size_t append = param->append; | ||
110 | |||
111 | /* walk through the directory content */ | ||
112 | while (rc == FORC_SUCCESS) { | ||
113 | errno = 0; /* distinguish failure from eod */ | ||
114 | struct dirent *entry = readdir(dir); | ||
115 | if (!entry) { | ||
116 | if (errno) { | ||
117 | rc = FORC_PATH_NOT_EXIST; | ||
118 | } | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | struct dirinfo info = dir_get_info(dir, entry); | ||
123 | if ((info.attribute & ATTR_DIRECTORY) && | ||
124 | is_dotdir_name(entry->d_name)) { | ||
125 | continue; /* skip these */ | ||
126 | } | ||
127 | |||
128 | /* append name to current directory */ | ||
129 | param->append = append + path_append(¶m->path[append], | ||
130 | PA_SEP_HARD, entry->d_name, | ||
131 | sizeof (param->path) - append); | ||
132 | |||
133 | if (fileop == FOC_DELETE) | ||
134 | { | ||
135 | param->processed++; | ||
136 | /* at this point we've already counted and never had a path too long | ||
137 | in the other branch so we 'should not' encounter one here either */ | ||
138 | |||
139 | if (param->processed > param->objects) | ||
140 | { | ||
141 | rc = FORC_UNKNOWN_FAILURE; | ||
142 | break; | ||
143 | } | ||
144 | |||
145 | if (info.attribute & ATTR_DIRECTORY) { | ||
146 | /* remove a subdirectory */ | ||
147 | rc = directory_fileop(param, fileop); | ||
148 | } else { | ||
149 | /* remove a file */ | ||
150 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | ||
151 | { | ||
152 | rc = FORC_CANCELLED; | ||
153 | break; | ||
154 | } | ||
155 | rc = remove(param->path); | ||
156 | } | ||
157 | } | ||
158 | else /* count objects */ | ||
159 | { | ||
160 | param->objects++; | ||
161 | |||
162 | if (param->append >= sizeof (param->path)) { | ||
163 | rc = FORC_PATH_TOO_LONG; | ||
164 | break; /* no space left in buffer */ | ||
165 | } | ||
166 | |||
167 | if (info.attribute & ATTR_DIRECTORY) { | ||
168 | /* remove a subdirectory */ | ||
169 | rc = directory_fileop(param, FOC_COUNT); | ||
170 | } else { | ||
171 | if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0)) | ||
172 | { | ||
173 | rc = FORC_CANCELLED; | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | param->append = append; /* other functions may use param, reset append */ | ||
179 | /* Remove basename we added above */ | ||
180 | param->path[append] = '\0'; | ||
181 | } | ||
182 | |||
183 | closedir(dir); | ||
184 | |||
185 | if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { | ||
186 | /* remove the now empty directory */ | ||
187 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | ||
188 | { | ||
189 | rc = FORC_CANCELLED; | ||
190 | } else { | ||
191 | rc = rmdir(param->path); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return rc; | ||
196 | } | ||
197 | |||
198 | static int check_count_fileobjects(struct file_op_params *param) | ||
199 | { | ||
200 | int rc = directory_fileop(param, FOC_COUNT); | ||
201 | DEBUGF("%s res:(%d) objects %d \n", __func__, rc, param->objects); | ||
202 | return rc; | ||
203 | } | ||
204 | |||
205 | static bool check_new_name(const char *basename) | ||
206 | { | ||
207 | /* at least prevent escapes out of the base directory from keyboard- | ||
208 | entered filenames; the file code should reject other invalidities */ | ||
209 | return *basename != '\0' && !strchr(basename, PATH_SEPCH) && | ||
210 | !is_dotdir_name(basename); | ||
211 | } | ||
212 | |||
213 | int create_dir(void) | ||
214 | { | ||
215 | int rc = FORC_UNKNOWN_FAILURE; | ||
216 | char dirname[MAX_PATH]; | ||
217 | size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, | ||
218 | sizeof (dirname)); | ||
219 | char *basename = dirname + pathlen; | ||
220 | |||
221 | if (pathlen >= sizeof (dirname)) { | ||
222 | /* Too long */ | ||
223 | } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) { | ||
224 | rc = FORC_CANCELLED; | ||
225 | } else if (check_new_name(basename)) { | ||
226 | rc = mkdir(dirname); | ||
227 | } | ||
228 | |||
229 | return rc; | ||
230 | } | ||
231 | |||
232 | /************************************************************************************/ | ||
233 | /* share code for file and directory deletion, saves space */ | ||
234 | static int delete_file_dir(struct file_op_params *param) | ||
235 | { | ||
236 | /* Note: delete_file_dir() will happily delete whatever | ||
237 | * path is passed (after confirmation) */ | ||
238 | if (confirm_delete_yesno(param->path) != YESNO_YES) { | ||
239 | return FORC_CANCELLED; | ||
240 | } | ||
241 | |||
242 | clear_screen_buffer(true); | ||
243 | poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects); | ||
244 | |||
245 | int rc = FORC_UNKNOWN_FAILURE; | ||
246 | |||
247 | if (param->is_dir) { /* if directory */ | ||
248 | cpu_boost(true); | ||
249 | rc = directory_fileop(param, FOC_DELETE); | ||
250 | cpu_boost(false); | ||
251 | } else { | ||
252 | rc = remove(param->path); | ||
253 | } | ||
254 | |||
255 | return rc; | ||
256 | } | ||
257 | |||
258 | int delete_fileobject(const char *selected_file) | ||
259 | { | ||
260 | struct file_op_params param; | ||
261 | if (init_file_op(¶m, selected_file, NULL)->is_dir == true) | ||
262 | { | ||
263 | int rc = check_count_fileobjects(¶m); | ||
264 | DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); | ||
265 | if (rc != FORC_SUCCESS) | ||
266 | return rc; | ||
267 | } | ||
268 | |||
269 | return delete_file_dir(¶m); | ||
270 | } | ||
271 | |||
272 | int rename_file(const char *selected_file) | ||
273 | { | ||
274 | int rc = FORC_UNKNOWN_FAILURE; | ||
275 | char newname[MAX_PATH]; | ||
276 | const char *oldbase, *selection = selected_file; | ||
277 | |||
278 | path_basename(selection, &oldbase); | ||
279 | size_t pathlen = oldbase - selection; | ||
280 | char *newbase = newname + pathlen; | ||
281 | |||
282 | if (strmemccpy(newname, selection, sizeof (newname)) == NULL) { | ||
283 | /* Too long */ | ||
284 | } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) { | ||
285 | rc = FORC_CANCELLED; | ||
286 | } else if (!strcmp(oldbase, newbase)) { | ||
287 | rc = FORC_NOOP; /* No change at all */ | ||
288 | } else if (check_new_name(newbase)) { | ||
289 | switch (relate(selection, newname)) | ||
290 | { | ||
291 | case RELATE_DIFFERENT: | ||
292 | if (file_exists(newname)) { | ||
293 | break; /* don't overwrite */ | ||
294 | } | ||
295 | /* Fall-through */ | ||
296 | case RELATE_SAME: | ||
297 | rc = rename(selection, newname); | ||
298 | break; | ||
299 | case RELATE_PREFIX: | ||
300 | default: | ||
301 | break; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | return rc; | ||
306 | } | ||
307 | |||
308 | static int move_by_rename(const char *src_path, | ||
309 | const char *dst_path, | ||
310 | unsigned int *pflags) | ||
311 | { | ||
312 | unsigned int flags = *pflags; | ||
313 | int rc = FORC_UNKNOWN_FAILURE; | ||
314 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | ||
315 | if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { | ||
316 | /* Just try to move the directory / file */ | ||
317 | if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { | ||
318 | rc = FORC_CANCELLED; | ||
319 | } else { | ||
320 | rc = rename(src_path, dst_path); | ||
321 | } | ||
322 | |||
323 | if (rc < 0) { | ||
324 | int errnum = errno; | ||
325 | if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { | ||
326 | /* Directory is not empty thus rename() will not do a quick | ||
327 | overwrite */ | ||
328 | break; | ||
329 | } | ||
330 | #ifdef HAVE_MULTIVOLUME | ||
331 | else if (errnum == EXDEV) { | ||
332 | /* Failed because cross volume rename doesn't work; force | ||
333 | a move instead */ | ||
334 | *pflags |= PASTE_EXDEV; | ||
335 | break; | ||
336 | } | ||
337 | #endif /* HAVE_MULTIVOLUME */ | ||
338 | } | ||
339 | } | ||
340 | |||
341 | break; | ||
342 | } | ||
343 | return rc; | ||
344 | } | ||
345 | |||
346 | /* Paste a file */ | ||
347 | static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) | ||
348 | { | ||
349 | /* Try renaming first */ | ||
350 | int rc = move_by_rename(src_path, dst_path, &flags); | ||
351 | if (rc == FORC_SUCCESS) | ||
352 | return rc; | ||
353 | |||
354 | /* See if we can get the plugin buffer for the file copy buffer */ | ||
355 | size_t buffersize; | ||
356 | char *buffer = (char *) plugin_get_buffer(&buffersize); | ||
357 | if (buffer == NULL || buffersize < 512) { | ||
358 | /* Not large enough, try for a disk sector worth of stack | ||
359 | instead */ | ||
360 | buffersize = 512; | ||
361 | buffer = (char *)alloca(buffersize); | ||
362 | } | ||
363 | |||
364 | if (buffer == NULL) { | ||
365 | return FORC_NO_BUFFER_AVAIL; | ||
366 | } | ||
367 | |||
368 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector | ||
369 | size */ | ||
370 | |||
371 | int src_fd = open(src_path, O_RDONLY); | ||
372 | if (src_fd >= 0) { | ||
373 | off_t src_sz = lseek(src_fd, 0, SEEK_END); | ||
374 | lseek(src_fd, 0, SEEK_SET); | ||
375 | |||
376 | int oflag = O_WRONLY|O_CREAT; | ||
377 | |||
378 | if (!(flags & PASTE_OVERWRITE)) { | ||
379 | oflag |= O_EXCL; | ||
380 | } | ||
381 | |||
382 | int dst_fd = open(dst_path, oflag, 0666); | ||
383 | if (dst_fd >= 0) { | ||
384 | off_t total_size = 0; | ||
385 | off_t next_cancel_test = 0; /* No excessive button polling */ | ||
386 | |||
387 | rc = FORC_SUCCESS; | ||
388 | |||
389 | while (rc == FORC_SUCCESS) { | ||
390 | if (total_size >= next_cancel_test) { | ||
391 | next_cancel_test = total_size + 0x10000; | ||
392 | if (poll_cancel_action(src_path, | ||
393 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | ||
394 | total_size, src_sz)) | ||
395 | { | ||
396 | rc = FORC_CANCELLED; | ||
397 | break; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | ssize_t bytesread = read(src_fd, buffer, buffersize); | ||
402 | if (bytesread <= 0) { | ||
403 | if (bytesread < 0) { | ||
404 | rc = FORC_READ_FAILURE; | ||
405 | } | ||
406 | /* else eof on buffer boundary; nothing to write */ | ||
407 | break; | ||
408 | } | ||
409 | |||
410 | ssize_t byteswritten = write(dst_fd, buffer, bytesread); | ||
411 | if (byteswritten < bytesread) { | ||
412 | /* Some I/O error */ | ||
413 | rc = FORC_WRITE_FAILURE; | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | total_size += byteswritten; | ||
418 | |||
419 | if (bytesread < (ssize_t)buffersize) { | ||
420 | /* EOF with trailing bytes */ | ||
421 | break; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | if (rc == FORC_SUCCESS) { | ||
426 | /* If overwriting, set the correct length if original was | ||
427 | longer */ | ||
428 | rc = ftruncate(dst_fd, total_size) * 10; | ||
429 | } | ||
430 | |||
431 | close(dst_fd); | ||
432 | |||
433 | if (rc != FORC_SUCCESS) { | ||
434 | /* Copy failed. Cleanup. */ | ||
435 | remove(dst_path); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | close(src_fd); | ||
440 | } | ||
441 | |||
442 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { | ||
443 | /* Remove the source file */ | ||
444 | rc = remove(src_path) * 10; | ||
445 | } | ||
446 | |||
447 | return rc; | ||
448 | } | ||
449 | |||
450 | /* Paste a directory */ | ||
451 | static int copy_move_directory(struct file_op_params *src, | ||
452 | struct file_op_params *dst, | ||
453 | unsigned int flags) | ||
454 | { | ||
455 | int rc = FORC_UNKNOWN_FAILURE; | ||
456 | |||
457 | DIR *srcdir = opendir(src->path); | ||
458 | |||
459 | if (srcdir) { | ||
460 | /* Make a directory to copy things to */ | ||
461 | rc = mkdir(dst->path) * 10; | ||
462 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { | ||
463 | /* Exists and overwrite was approved */ | ||
464 | rc = FORC_SUCCESS; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | size_t srcap = src->append, dstap = dst->append; | ||
469 | |||
470 | /* Walk through the directory content; this loop will exit as soon as | ||
471 | there's a problem */ | ||
472 | while (rc == FORC_SUCCESS) { | ||
473 | errno = 0; /* Distinguish failure from eod */ | ||
474 | struct dirent *entry = readdir(srcdir); | ||
475 | if (!entry) { | ||
476 | if (errno) { | ||
477 | rc = FORC_PATH_NOT_EXIST; | ||
478 | } | ||
479 | break; | ||
480 | } | ||
481 | |||
482 | struct dirinfo info = dir_get_info(srcdir, entry); | ||
483 | if ((info.attribute & ATTR_DIRECTORY) && | ||
484 | is_dotdir_name(entry->d_name)) { | ||
485 | continue; /* Skip these */ | ||
486 | } | ||
487 | |||
488 | /* Append names to current directories */ | ||
489 | src->append = srcap + | ||
490 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, | ||
491 | sizeof(src->path) - srcap); | ||
492 | |||
493 | dst->append = dstap + | ||
494 | path_append(&dst->path[dstap], PA_SEP_HARD, entry->d_name, | ||
495 | sizeof (dst->path) - dstap); | ||
496 | /* src length was already checked by check_count_fileobjects() */ | ||
497 | if (dst->append >= sizeof (dst->path)) { | ||
498 | rc = FORC_PATH_TOO_LONG; /* No space left in buffer */ | ||
499 | break; | ||
500 | } | ||
501 | |||
502 | src->processed++; | ||
503 | if (src->processed > src->objects) | ||
504 | { | ||
505 | rc = FORC_UNKNOWN_FAILURE; | ||
506 | break; | ||
507 | } | ||
508 | |||
509 | if (poll_cancel_action(src->path, | ||
510 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | ||
511 | src->processed, src->objects)) | ||
512 | { | ||
513 | rc = FORC_CANCELLED; | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | DEBUGF("Copy %s to %s\n", src->path, dst->path); | ||
518 | |||
519 | if (info.attribute & ATTR_DIRECTORY) { | ||
520 | /* Copy/move a subdirectory */ | ||
521 | rc = copy_move_directory(src, dst, flags); /* recursion */; | ||
522 | } else { | ||
523 | /* Copy/move a file */ | ||
524 | rc = copy_move_file(src->path, dst->path, flags); | ||
525 | } | ||
526 | |||
527 | /* Remove basenames we added above */ | ||
528 | src->path[srcap] = '\0'; | ||
529 | dst->path[dstap] = '\0'; | ||
530 | } | ||
531 | |||
532 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { | ||
533 | /* Remove the now empty directory */ | ||
534 | rc = rmdir(src->path) * 10; | ||
535 | } | ||
536 | |||
537 | closedir(srcdir); | ||
538 | return rc; | ||
539 | } | ||
540 | |||
541 | int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned int flags) | ||
542 | { | ||
543 | if (!src_path[0]) | ||
544 | return FORC_NOOP; | ||
545 | |||
546 | int rc = FORC_UNKNOWN_FAILURE; | ||
547 | |||
548 | struct file_op_params src, dst; | ||
549 | |||
550 | /* Figure out the name of the selection */ | ||
551 | const char *nameptr; | ||
552 | path_basename(src_path, &nameptr); | ||
553 | |||
554 | /* Final target is current directory plus name of selection */ | ||
555 | init_file_op(&dst, dst_path, nameptr); | ||
556 | |||
557 | switch (dst.append < sizeof (dst.path) ? | ||
558 | relate(src_path, dst.path) : FORC_PATH_TOO_LONG) | ||
559 | { | ||
560 | case RELATE_SAME: | ||
561 | rc = FORC_NOOP; | ||
562 | break; | ||
563 | |||
564 | case RELATE_DIFFERENT: | ||
565 | if (file_exists(dst.path)) { | ||
566 | /* If user chooses not to overwrite, cancel */ | ||
567 | if (confirm_overwrite_yesno() == YESNO_NO) { | ||
568 | rc = FORC_NOOVERWRT; | ||
569 | break; | ||
570 | } | ||
571 | |||
572 | flags |= PASTE_OVERWRITE; | ||
573 | } | ||
574 | |||
575 | /* Now figure out what we're doing */ | ||
576 | cpu_boost(true); | ||
577 | |||
578 | init_file_op(&src, src_path, NULL); | ||
579 | |||
580 | if (src.is_dir) { | ||
581 | /* Copy or move a subdirectory */ | ||
582 | |||
583 | if (src.append < sizeof (src.path)) { | ||
584 | /* Try renaming first */ | ||
585 | rc = move_by_rename(src.path, dst.path, &flags); | ||
586 | if (rc != FORC_SUCCESS && rc != FORC_CANCELLED) { | ||
587 | if (check_count_fileobjects(&src) == FORC_SUCCESS) { | ||
588 | rc = copy_move_directory(&src, &dst, flags); | ||
589 | } | ||
590 | } | ||
591 | } | ||
592 | } else { | ||
593 | /* Copy or move a file */ | ||
594 | rc = copy_move_file(src_path, dst.path, flags); | ||
595 | } | ||
596 | |||
597 | cpu_boost(false); | ||
598 | break; | ||
599 | |||
600 | case RELATE_PREFIX: | ||
601 | default: /* Some other relation / failure */ | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | return rc; | ||
606 | } | ||