diff options
Diffstat (limited to 'apps/fileop.c')
-rw-r--r-- | apps/fileop.c | 475 |
1 files changed, 255 insertions, 220 deletions
diff --git a/apps/fileop.c b/apps/fileop.c index 0d2dc774b9..bcd5202cf4 100644 --- a/apps/fileop.c +++ b/apps/fileop.c | |||
@@ -1,10 +1,10 @@ | |||
1 | /*************************************************************************** | 1 | /*************************************************************************** |
2 | * __________ __ ___. | 2 | * __________ __ ___. |
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 Björn Stenberg | 10 | * Copyright (C) 2002 Björn Stenberg |
@@ -26,6 +26,7 @@ | |||
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include "string-extra.h" | 27 | #include "string-extra.h" |
28 | #include "debug.h" | 28 | #include "debug.h" |
29 | #include "powermgmt.h" | ||
29 | 30 | ||
30 | #include "misc.h" | 31 | #include "misc.h" |
31 | #include "plugin.h" | 32 | #include "plugin.h" |
@@ -44,38 +45,77 @@ | |||
44 | struct file_op_params | 45 | struct file_op_params |
45 | { | 46 | { |
46 | char path[MAX_PATH]; /* Buffer for full path */ | 47 | char path[MAX_PATH]; /* Buffer for full path */ |
48 | const char* toplevel_name; | ||
47 | bool is_dir; | 49 | bool is_dir; |
48 | int objects; /* how many files and subdirectories*/ | 50 | int objects; /* how many files and subdirectories*/ |
49 | int processed; | 51 | int processed; |
52 | unsigned long long total_size; | ||
53 | unsigned long long processed_size; | ||
50 | size_t append; /* Append position in 'path' for stack push */ | 54 | size_t append; /* Append position in 'path' for stack push */ |
55 | size_t extra_len; /* Length added by dst path compared to src */ | ||
51 | }; | 56 | }; |
52 | 57 | ||
53 | static bool poll_cancel_action(const char *path, int operation, int current, int total) | 58 | static int prompt_name(char* buf, size_t bufsz) |
54 | { | 59 | { |
55 | const char *op_str = ""; | 60 | if (kbd_input(buf, bufsz, NULL) < 0) |
56 | 61 | return FORC_CANCELLED; | |
57 | clear_screen_buffer(false); | 62 | /* at least prevent escapes out of the base directory from keyboard- |
58 | 63 | entered filenames; the file code should reject other invalidities */ | |
59 | if (operation == FOC_COPY) | 64 | if (*buf != '\0' && !strchr(buf, PATH_SEPCH) && !is_dotdir_name(buf)) |
60 | op_str = str(LANG_COPYING); | 65 | return FORC_SUCCESS; |
61 | else if (operation == FOC_MOVE) | 66 | return FORC_UNKNOWN_FAILURE; |
62 | op_str = str(LANG_MOVING); | 67 | } |
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 | ||
68 | path_basename(path, &path); | 69 | static bool poll_cancel_action(int operation, struct file_op_params *param) |
70 | { | ||
71 | static unsigned long last_tick; | ||
69 | 72 | ||
70 | if (total <= 0) | 73 | if (operation == FOC_COUNT) |
71 | splashf(0, "%s (%d) %s", op_str, current, path); | 74 | { |
75 | if (param->objects <= 1) | ||
76 | last_tick = current_tick; | ||
77 | else if (TIME_AFTER(current_tick, last_tick + HZ/2)) | ||
78 | { | ||
79 | clear_screen_buffer(false); | ||
80 | splashf(0, "%s (%d)", str(LANG_SCANNING_DISK), param->objects); | ||
81 | last_tick = current_tick; | ||
82 | } | ||
83 | } | ||
72 | else | 84 | else |
73 | splash_progress(current, total, "%s %s", op_str, path); | 85 | { |
86 | const char *op_str = (operation == FOC_DELETE) ? str(LANG_DELETING) : | ||
87 | (operation == FOC_COPY) ? str(LANG_COPYING) : | ||
88 | str(LANG_MOVING); | ||
74 | 89 | ||
90 | if ((operation == FOC_DELETE || !param->total_size) && | ||
91 | param->objects > 0) | ||
92 | { | ||
93 | splash_progress(param->processed, param->objects, | ||
94 | "%s %s", op_str, param->toplevel_name); | ||
95 | } | ||
96 | else if (param->total_size >= 10 * 1024 * 1024) | ||
97 | { | ||
98 | int total_shft = (int) (param->total_size >> 15); | ||
99 | int current_shft = (int) (param->processed_size >> 15); | ||
100 | splash_progress(current_shft, total_shft, | ||
101 | "%s %s (%d MiB)\n%d MiB", | ||
102 | op_str, param->toplevel_name, | ||
103 | total_shft >> 5, current_shft >> 5); | ||
104 | } | ||
105 | else if (param->total_size >= 1024) | ||
106 | { | ||
107 | int total_kib = (int) (param->total_size >> 10); | ||
108 | int current_kib = (int) (param->processed_size >> 10); | ||
109 | splash_progress(current_kib, total_kib, | ||
110 | "%s %s (%d KiB)\n%d KiB", | ||
111 | op_str, param->toplevel_name, | ||
112 | total_kib, current_kib); | ||
113 | } | ||
114 | } | ||
75 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); | 115 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); |
76 | } | 116 | } |
77 | 117 | ||
78 | static struct file_op_params* init_file_op(struct file_op_params *param, | 118 | static void init_file_op(struct file_op_params *param, |
79 | const char *basename, | 119 | const char *basename, |
80 | const char *selected_file) | 120 | const char *selected_file) |
81 | { | 121 | { |
@@ -83,15 +123,21 @@ static struct file_op_params* init_file_op(struct file_op_params *param, | |||
83 | if (selected_file == NULL) | 123 | if (selected_file == NULL) |
84 | { | 124 | { |
85 | param->append = strlcpy(param->path, basename, sizeof (param->path)); | 125 | param->append = strlcpy(param->path, basename, sizeof (param->path)); |
126 | path_basename(basename, &basename); | ||
127 | param->toplevel_name = basename; | ||
86 | } | 128 | } |
87 | else | 129 | else |
130 | { | ||
88 | param->append = path_append(param->path, basename, | 131 | param->append = path_append(param->path, basename, |
89 | selected_file, sizeof (param->path)); | 132 | selected_file, sizeof (param->path)); |
133 | param->toplevel_name = selected_file; | ||
134 | } | ||
90 | param->is_dir = dir_exists(param->path); | 135 | param->is_dir = dir_exists(param->path); |
136 | param->extra_len = 0; | ||
91 | param->objects = 0; /* how many files and subdirectories*/ | 137 | param->objects = 0; /* how many files and subdirectories*/ |
92 | param->processed = 0; | 138 | param->processed = 0; |
93 | 139 | param->total_size = 0; | |
94 | return param; | 140 | param->processed_size = 0; |
95 | } | 141 | } |
96 | 142 | ||
97 | /* counts file objects, deletes file objects */ | 143 | /* counts file objects, deletes file objects */ |
@@ -144,10 +190,10 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f | |||
144 | 190 | ||
145 | if (info.attribute & ATTR_DIRECTORY) { | 191 | if (info.attribute & ATTR_DIRECTORY) { |
146 | /* remove a subdirectory */ | 192 | /* remove a subdirectory */ |
147 | rc = directory_fileop(param, fileop); | 193 | rc = directory_fileop(param, fileop); /* recursion */ |
148 | } else { | 194 | } else { |
149 | /* remove a file */ | 195 | /* remove a file */ |
150 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | 196 | if (poll_cancel_action(FOC_DELETE, param)) |
151 | { | 197 | { |
152 | rc = FORC_CANCELLED; | 198 | rc = FORC_CANCELLED; |
153 | break; | 199 | break; |
@@ -158,21 +204,22 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f | |||
158 | else /* count objects */ | 204 | else /* count objects */ |
159 | { | 205 | { |
160 | param->objects++; | 206 | param->objects++; |
207 | param->total_size += info.size; | ||
161 | 208 | ||
162 | if (param->append >= sizeof (param->path)) { | 209 | if (poll_cancel_action(FOC_COUNT, param)) |
210 | { | ||
211 | rc = FORC_CANCELLED; | ||
212 | break; | ||
213 | } | ||
214 | |||
215 | if (param->append + param->extra_len >= sizeof (param->path)) { | ||
163 | rc = FORC_PATH_TOO_LONG; | 216 | rc = FORC_PATH_TOO_LONG; |
164 | break; /* no space left in buffer */ | 217 | break; /* no space left in buffer */ |
165 | } | 218 | } |
166 | 219 | ||
167 | if (info.attribute & ATTR_DIRECTORY) { | 220 | if (info.attribute & ATTR_DIRECTORY) { |
168 | /* remove a subdirectory */ | 221 | /* enter subdirectory */ |
169 | rc = directory_fileop(param, FOC_COUNT); | 222 | rc = directory_fileop(param, FOC_COUNT); /* recursion */ |
170 | } else { | ||
171 | if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0)) | ||
172 | { | ||
173 | rc = FORC_CANCELLED; | ||
174 | break; | ||
175 | } | ||
176 | } | 223 | } |
177 | } | 224 | } |
178 | param->append = append; /* other functions may use param, reset append */ | 225 | param->append = append; /* other functions may use param, reset append */ |
@@ -184,7 +231,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f | |||
184 | 231 | ||
185 | if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { | 232 | if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { |
186 | /* remove the now empty directory */ | 233 | /* remove the now empty directory */ |
187 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | 234 | if (poll_cancel_action(FOC_DELETE, param)) |
188 | { | 235 | { |
189 | rc = FORC_CANCELLED; | 236 | rc = FORC_CANCELLED; |
190 | } else { | 237 | } else { |
@@ -195,161 +242,54 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f | |||
195 | return rc; | 242 | return rc; |
196 | } | 243 | } |
197 | 244 | ||
245 | /* Walk a directory tree and count the number of objects (dirs & files) | ||
246 | * also check that enough resources exist to do an operation */ | ||
198 | static int check_count_fileobjects(struct file_op_params *param) | 247 | static int check_count_fileobjects(struct file_op_params *param) |
199 | { | 248 | { |
249 | cpu_boost(true); | ||
200 | int rc = directory_fileop(param, FOC_COUNT); | 250 | int rc = directory_fileop(param, FOC_COUNT); |
251 | cpu_boost(false); | ||
201 | DEBUGF("%s res:(%d) objects %d \n", __func__, rc, param->objects); | 252 | DEBUGF("%s res:(%d) objects %d \n", __func__, rc, param->objects); |
202 | return rc; | 253 | return rc; |
203 | } | 254 | } |
204 | 255 | ||
205 | static bool check_new_name(const char *basename) | 256 | /* Attempt to just rename a file or directory */ |
206 | { | 257 | static int move_by_rename(struct file_op_params *src, |
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, | 258 | const char *dst_path, |
310 | unsigned int *pflags) | 259 | unsigned int *pflags) |
311 | { | 260 | { |
312 | unsigned int flags = *pflags; | 261 | unsigned int flags = *pflags; |
313 | int rc = FORC_UNKNOWN_FAILURE; | 262 | int rc = FORC_UNKNOWN_FAILURE; |
314 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | 263 | reset_poweroff_timer(); |
264 | if (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | ||
315 | if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { | 265 | if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { |
316 | /* Just try to move the directory / file */ | 266 | /* Just try to move the directory / file */ |
317 | if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { | 267 | rc = rename(src->path, dst_path); |
318 | rc = FORC_CANCELLED; | 268 | #ifdef HAVE_MULTIVOLUME |
319 | } else { | 269 | if (rc < FORC_SUCCESS && errno == EXDEV) { |
320 | rc = rename(src_path, dst_path); | 270 | /* Failed because cross volume rename doesn't work */ |
321 | } | 271 | *pflags |= PASTE_EXDEV; /* force a move instead */ |
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 | } | 272 | } |
273 | #endif /* HAVE_MULTIVOLUME */ | ||
274 | /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { | ||
275 | * Directory is not empty thus rename() will not do a quick overwrite */ | ||
339 | } | 276 | } |
340 | 277 | ||
341 | break; | ||
342 | } | 278 | } |
343 | return rc; | 279 | return rc; |
344 | } | 280 | } |
345 | 281 | ||
346 | /* Paste a file */ | 282 | /* Paste a file */ |
347 | static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) | 283 | static int copy_move_file(struct file_op_params *src, const char *dst_path, |
284 | unsigned int flags) | ||
348 | { | 285 | { |
349 | /* Try renaming first */ | 286 | /* Try renaming first */ |
350 | int rc = move_by_rename(src_path, dst_path, &flags); | 287 | int rc = move_by_rename(src, dst_path, &flags); |
351 | if (rc == FORC_SUCCESS) | 288 | if (rc == FORC_SUCCESS) |
289 | { | ||
290 | src->total_size = 0; /* switch from counting size to number of items */ | ||
352 | return rc; | 291 | return rc; |
292 | } | ||
353 | 293 | ||
354 | /* See if we can get the plugin buffer for the file copy buffer */ | 294 | /* See if we can get the plugin buffer for the file copy buffer */ |
355 | size_t buffersize; | 295 | size_t buffersize; |
@@ -365,12 +305,13 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
365 | return FORC_NO_BUFFER_AVAIL; | 305 | return FORC_NO_BUFFER_AVAIL; |
366 | } | 306 | } |
367 | 307 | ||
368 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector | 308 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */ |
369 | size */ | ||
370 | 309 | ||
371 | int src_fd = open(src_path, O_RDONLY); | 310 | int src_fd = open(src->path, O_RDONLY); |
372 | if (src_fd >= 0) { | 311 | if (src_fd >= 0) { |
373 | off_t src_sz = lseek(src_fd, 0, SEEK_END); | 312 | off_t src_sz = lseek(src_fd, 0, SEEK_END); |
313 | if (!src->total_size && !src->processed) /* single file copy */ | ||
314 | src->total_size = src_sz; | ||
374 | lseek(src_fd, 0, SEEK_SET); | 315 | lseek(src_fd, 0, SEEK_SET); |
375 | 316 | ||
376 | int oflag = O_WRONLY|O_CREAT; | 317 | int oflag = O_WRONLY|O_CREAT; |
@@ -389,9 +330,8 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
389 | while (rc == FORC_SUCCESS) { | 330 | while (rc == FORC_SUCCESS) { |
390 | if (total_size >= next_cancel_test) { | 331 | if (total_size >= next_cancel_test) { |
391 | next_cancel_test = total_size + 0x10000; | 332 | next_cancel_test = total_size + 0x10000; |
392 | if (poll_cancel_action(src_path, | 333 | if (poll_cancel_action(!(flags & PASTE_COPY) ? |
393 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | 334 | FOC_MOVE : FOC_COPY, src)) |
394 | total_size, src_sz)) | ||
395 | { | 335 | { |
396 | rc = FORC_CANCELLED; | 336 | rc = FORC_CANCELLED; |
397 | break; | 337 | break; |
@@ -415,6 +355,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
415 | } | 355 | } |
416 | 356 | ||
417 | total_size += byteswritten; | 357 | total_size += byteswritten; |
358 | src->processed_size += byteswritten; | ||
418 | 359 | ||
419 | if (bytesread < (ssize_t)buffersize) { | 360 | if (bytesread < (ssize_t)buffersize) { |
420 | /* EOF with trailing bytes */ | 361 | /* EOF with trailing bytes */ |
@@ -423,9 +364,12 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
423 | } | 364 | } |
424 | 365 | ||
425 | if (rc == FORC_SUCCESS) { | 366 | if (rc == FORC_SUCCESS) { |
426 | /* If overwriting, set the correct length if original was | 367 | if (total_size != src_sz) |
427 | longer */ | 368 | rc = FORC_UNKNOWN_FAILURE; |
428 | rc = ftruncate(dst_fd, total_size) * 10; | 369 | else { |
370 | /* If overwriting, set the correct length if original was longer */ | ||
371 | rc = ftruncate(dst_fd, total_size) * 10; | ||
372 | } | ||
429 | } | 373 | } |
430 | 374 | ||
431 | close(dst_fd); | 375 | close(dst_fd); |
@@ -441,7 +385,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
441 | 385 | ||
442 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { | 386 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { |
443 | /* Remove the source file */ | 387 | /* Remove the source file */ |
444 | rc = remove(src_path) * 10; | 388 | rc = remove(src->path) * 10; |
445 | } | 389 | } |
446 | 390 | ||
447 | return rc; | 391 | return rc; |
@@ -449,20 +393,19 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i | |||
449 | 393 | ||
450 | /* Paste a directory */ | 394 | /* Paste a directory */ |
451 | static int copy_move_directory(struct file_op_params *src, | 395 | static int copy_move_directory(struct file_op_params *src, |
452 | struct file_op_params *dst, | 396 | struct file_op_params *dst, |
453 | unsigned int flags) | 397 | unsigned int flags) |
454 | { | 398 | { |
455 | int rc = FORC_UNKNOWN_FAILURE; | ||
456 | |||
457 | DIR *srcdir = opendir(src->path); | 399 | DIR *srcdir = opendir(src->path); |
458 | 400 | ||
459 | if (srcdir) { | 401 | if (!srcdir) |
460 | /* Make a directory to copy things to */ | 402 | return FORC_PATH_NOT_EXIST; |
461 | rc = mkdir(dst->path) * 10; | 403 | |
462 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { | 404 | /* Make a directory to copy things to */ |
463 | /* Exists and overwrite was approved */ | 405 | int rc = mkdir(dst->path) * 10; |
464 | rc = FORC_SUCCESS; | 406 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { |
465 | } | 407 | /* Exists and overwrite was approved */ |
408 | rc = FORC_SUCCESS; | ||
466 | } | 409 | } |
467 | 410 | ||
468 | size_t srcap = src->append, dstap = dst->append; | 411 | size_t srcap = src->append, dstap = dst->append; |
@@ -488,7 +431,7 @@ static int copy_move_directory(struct file_op_params *src, | |||
488 | /* Append names to current directories */ | 431 | /* Append names to current directories */ |
489 | src->append = srcap + | 432 | src->append = srcap + |
490 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, | 433 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, |
491 | sizeof(src->path) - srcap); | 434 | sizeof (src->path) - srcap); |
492 | 435 | ||
493 | dst->append = dstap + | 436 | dst->append = dstap + |
494 | path_append(&dst->path[dstap], PA_SEP_HARD, entry->d_name, | 437 | path_append(&dst->path[dstap], PA_SEP_HARD, entry->d_name, |
@@ -506,9 +449,8 @@ static int copy_move_directory(struct file_op_params *src, | |||
506 | break; | 449 | break; |
507 | } | 450 | } |
508 | 451 | ||
509 | if (poll_cancel_action(src->path, | 452 | if (poll_cancel_action(!(flags & PASTE_COPY) ? |
510 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | 453 | FOC_MOVE : FOC_COPY, src)) |
511 | src->processed, src->objects)) | ||
512 | { | 454 | { |
513 | rc = FORC_CANCELLED; | 455 | rc = FORC_CANCELLED; |
514 | break; | 456 | break; |
@@ -517,11 +459,12 @@ static int copy_move_directory(struct file_op_params *src, | |||
517 | DEBUGF("Copy %s to %s\n", src->path, dst->path); | 459 | DEBUGF("Copy %s to %s\n", src->path, dst->path); |
518 | 460 | ||
519 | if (info.attribute & ATTR_DIRECTORY) { | 461 | if (info.attribute & ATTR_DIRECTORY) { |
462 | src->processed_size += info.size; | ||
520 | /* Copy/move a subdirectory */ | 463 | /* Copy/move a subdirectory */ |
521 | rc = copy_move_directory(src, dst, flags); /* recursion */; | 464 | rc = copy_move_directory(src, dst, flags); /* recursion */; |
522 | } else { | 465 | } else { |
523 | /* Copy/move a file */ | 466 | /* Copy/move a file */ |
524 | rc = copy_move_file(src->path, dst->path, flags); | 467 | rc = copy_move_file(src, dst->path, flags); |
525 | } | 468 | } |
526 | 469 | ||
527 | /* Remove basenames we added above */ | 470 | /* Remove basenames we added above */ |
@@ -538,13 +481,16 @@ static int copy_move_directory(struct file_op_params *src, | |||
538 | return rc; | 481 | return rc; |
539 | } | 482 | } |
540 | 483 | ||
484 | /************************************************************************************/ | ||
485 | /* PUBLIC FUNCTIONS */ | ||
486 | /************************************************************************************/ | ||
487 | |||
488 | /* Copy or move a file or directory see: file_op_flags */ | ||
541 | int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned int flags) | 489 | int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned int flags) |
542 | { | 490 | { |
543 | if (!src_path[0]) | 491 | if (!src_path[0]) |
544 | return FORC_NOOP; | 492 | return FORC_NOOP; |
545 | 493 | ||
546 | int rc = FORC_UNKNOWN_FAILURE; | ||
547 | |||
548 | struct file_op_params src, dst; | 494 | struct file_op_params src, dst; |
549 | 495 | ||
550 | /* Figure out the name of the selection */ | 496 | /* Figure out the name of the selection */ |
@@ -553,54 +499,143 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in | |||
553 | 499 | ||
554 | /* Final target is current directory plus name of selection */ | 500 | /* Final target is current directory plus name of selection */ |
555 | init_file_op(&dst, dst_path, nameptr); | 501 | init_file_op(&dst, dst_path, nameptr); |
502 | if (dst.append >= sizeof (dst.path)) | ||
503 | return FORC_PATH_TOO_LONG; | ||
556 | 504 | ||
557 | switch (dst.append < sizeof (dst.path) ? | 505 | int rel = relate(src_path, dst.path); |
558 | relate(src_path, dst.path) : FORC_PATH_TOO_LONG) | 506 | if (rel == RELATE_SAME) |
559 | { | 507 | return FORC_NOOP; |
560 | case RELATE_SAME: | ||
561 | rc = FORC_NOOP; | ||
562 | break; | ||
563 | 508 | ||
564 | case RELATE_DIFFERENT: | 509 | if (rel == RELATE_DIFFERENT) { |
510 | int rc; | ||
565 | if (file_exists(dst.path)) { | 511 | if (file_exists(dst.path)) { |
566 | /* If user chooses not to overwrite, cancel */ | 512 | /* If user chooses not to overwrite, cancel */ |
567 | if (confirm_overwrite_yesno() == YESNO_NO) { | 513 | if (confirm_overwrite_yesno() == YESNO_NO) { |
568 | rc = FORC_NOOVERWRT; | 514 | return FORC_NOOVERWRT; |
569 | break; | ||
570 | } | 515 | } |
571 | 516 | ||
572 | flags |= PASTE_OVERWRITE; | 517 | flags |= PASTE_OVERWRITE; |
573 | } | 518 | } |
574 | 519 | ||
520 | init_file_op(&src, src_path, NULL); | ||
521 | if (src.append >= sizeof (src.path)) | ||
522 | return FORC_PATH_TOO_LONG; | ||
575 | /* Now figure out what we're doing */ | 523 | /* Now figure out what we're doing */ |
576 | cpu_boost(true); | 524 | cpu_boost(true); |
577 | |||
578 | init_file_op(&src, src_path, NULL); | ||
579 | |||
580 | if (src.is_dir) { | 525 | if (src.is_dir) { |
581 | /* Copy or move a subdirectory */ | 526 | /* Copy or move a subdirectory */ |
582 | 527 | /* Try renaming first */ | |
583 | if (src.append < sizeof (src.path)) { | 528 | rc = move_by_rename(&src, dst.path, &flags); |
584 | /* Try renaming first */ | 529 | if (rc < FORC_SUCCESS) { |
585 | rc = move_by_rename(src.path, dst.path, &flags); | 530 | int extra_len = dst.append - src.append; |
586 | if (rc != FORC_SUCCESS && rc != FORC_CANCELLED) { | 531 | if (extra_len > 0) |
587 | if (check_count_fileobjects(&src) == FORC_SUCCESS) { | 532 | src.extra_len = extra_len; |
588 | rc = copy_move_directory(&src, &dst, flags); | 533 | |
589 | } | 534 | rc = check_count_fileobjects(&src); |
535 | if (rc == FORC_SUCCESS) { | ||
536 | rc = copy_move_directory(&src, &dst, flags); | ||
590 | } | 537 | } |
591 | } | 538 | } |
592 | } else { | 539 | } else { |
593 | /* Copy or move a file */ | 540 | /* Copy or move a file */ |
594 | rc = copy_move_file(src_path, dst.path, flags); | 541 | rc = copy_move_file(&src, dst.path, flags); |
595 | } | 542 | } |
596 | 543 | ||
597 | cpu_boost(false); | 544 | cpu_boost(false); |
598 | break; | 545 | DEBUGF("%s res: %d, ct: %d/%d %s\n", |
546 | __func__, rc, src.objects, src.processed, src.path); | ||
547 | return rc; | ||
548 | } | ||
549 | |||
550 | /* Else Some other relation / failure */ | ||
551 | DEBUGF("%s res: %d, rel: %d\n", __func__, FORC_UNKNOWN_FAILURE, rel); | ||
552 | return FORC_UNKNOWN_FAILURE; | ||
553 | } | ||
554 | |||
555 | int create_dir(void) | ||
556 | { | ||
557 | int rc; | ||
558 | char dirname[MAX_PATH]; | ||
559 | size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, | ||
560 | sizeof (dirname)); | ||
561 | char *basename = dirname + pathlen; | ||
562 | |||
563 | if (pathlen >= sizeof (dirname)) | ||
564 | return FORC_PATH_TOO_LONG; | ||
565 | |||
566 | rc = prompt_name(basename, sizeof (dirname) - pathlen); | ||
567 | if (rc == FORC_SUCCESS) | ||
568 | rc = mkdir(dirname) * 10; | ||
569 | return rc; | ||
570 | } | ||
571 | |||
572 | /* share code for file and directory deletion, saves space */ | ||
573 | int delete_fileobject(const char *selected_file) | ||
574 | { | ||
575 | int rc; | ||
576 | struct file_op_params param; | ||
577 | init_file_op(¶m, selected_file, NULL); | ||
578 | if (param.append >= sizeof (param.path)) | ||
579 | return FORC_PATH_TOO_LONG; | ||
599 | 580 | ||
600 | case RELATE_PREFIX: | 581 | /* Note: delete_fileobject() will happily delete whatever |
601 | default: /* Some other relation / failure */ | 582 | * path is passed (after confirmation) */ |
602 | break; | 583 | if (confirm_delete_yesno(param.path) != YESNO_YES) { |
584 | return FORC_CANCELLED; | ||
585 | } | ||
586 | |||
587 | if (param.is_dir) { | ||
588 | int rc = check_count_fileobjects(¶m); | ||
589 | DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); | ||
590 | if (rc != FORC_SUCCESS) | ||
591 | return rc; | ||
592 | } | ||
593 | |||
594 | clear_screen_buffer(true); | ||
595 | |||
596 | if (param.is_dir) { /* if directory */ | ||
597 | cpu_boost(true); | ||
598 | rc = directory_fileop(¶m, FOC_DELETE); | ||
599 | cpu_boost(false); | ||
600 | } else { | ||
601 | rc = remove(param.path) * 10; | ||
603 | } | 602 | } |
604 | 603 | ||
605 | return rc; | 604 | return rc; |
606 | } | 605 | } |
606 | |||
607 | int rename_file(const char *selected_file) | ||
608 | { | ||
609 | int rc; | ||
610 | char newname[MAX_PATH]; | ||
611 | const char *oldbase, *selection = selected_file; | ||
612 | |||
613 | path_basename(selection, &oldbase); | ||
614 | size_t pathlen = oldbase - selection; | ||
615 | char *newbase = newname + pathlen; | ||
616 | |||
617 | if (strmemccpy(newname, selection, sizeof (newname)) == NULL) | ||
618 | return FORC_PATH_TOO_LONG; | ||
619 | |||
620 | rc = prompt_name(newbase, sizeof (newname) - pathlen); | ||
621 | |||
622 | if (rc != FORC_SUCCESS) | ||
623 | return rc; | ||
624 | |||
625 | if (!strcmp(oldbase, newbase)) | ||
626 | return FORC_NOOP; /* No change at all */ | ||
627 | |||
628 | int rel = relate(selection, newname); | ||
629 | if (rel == RELATE_DIFFERENT) | ||
630 | { | ||
631 | if (file_exists(newname)) { /* don't overwrite */ | ||
632 | return FORC_PATH_EXISTS; | ||
633 | } | ||
634 | return rename(selection, newname) * 10; | ||
635 | } | ||
636 | if (rel == RELATE_SAME) | ||
637 | return rename(selection, newname) * 10; | ||
638 | |||
639 | /* Else Some other relation / failure */ | ||
640 | return FORC_UNKNOWN_FAILURE; | ||
641 | } | ||