fix panel_scroll_pages start page
[midnight-commander.git] / src / file.c
bloba6c1f650ec6423f02c0da5bd54adaef2fad7b620
1 /* {{{ Copyright */
3 /* File managing. Important notes on this file:
5 About the use of dialogs in this file:
6 If you want to add a new dialog box (or call a routine that pops
7 up a dialog box), you have to provide a wrapper for background
8 operations (ie, background operations have to up-call to the parent
9 process).
11 For example, instead of using the message() routine, in this
12 file, you should use one of the stubs that call message with the
13 proper number of arguments (ie, message_1s, message_2s and so on).
15 Actually, that is a rule that should be followed by any routines
16 that may be called from this module.
20 /* File managing
21 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
23 Written by: 1994, 1995 Janne Kukonlehto
24 1994, 1995 Fred Leeflang
25 1994, 1995, 1996 Miguel de Icaza
26 1995, 1996 Jakub Jelinek
27 1997 Norbert Warmuth
28 1998 Pavel Machek
30 The copy code was based in GNU's cp, and was written by:
31 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
33 The move code was based in GNU's mv, and was written by:
34 Mike Parker and David MacKenzie.
36 Janne Kukonlehto added much error recovery to them for being used
37 in an interactive program.
39 This program is free software; you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation; either version 2 of the License, or
42 (at your option) any later version.
44 This program is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
49 You should have received a copy of the GNU General Public License
50 along with this program; if not, write to the Free Software
51 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
53 /* }}} */
55 /* {{{ Include files */
57 #include <config.h>
58 /* Hack: the vfs code should not rely on this */
59 #define WITH_FULL_PATHS 1
61 #include <sys/types.h>
62 #include <stdio.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <string.h>
66 #ifdef HAVE_UNISTD_H
67 # include <unistd.h>
68 #endif /* HAVE_UNISTD_H */
69 #include <sys/stat.h>
71 #include "global.h"
72 #include "tty.h"
73 #include "eregex.h"
74 #include "dialog.h"
75 #include "setup.h"
76 /* Needed by query_replace */
77 #include "color.h"
78 #include "win.h"
79 #include "dlg.h"
80 #include "widget.h"
81 #define WANT_WIDGETS
82 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
83 #include "layout.h"
84 #include "widget.h"
85 #include "wtools.h"
87 /* Needed for current_panel, other_panel and WTree */
88 #include "dir.h"
89 #include "panel.h"
90 #include "file.h"
91 #include "filegui.h"
92 #include "tree.h"
93 #include "key.h"
94 #include "../vfs/vfs.h"
96 /* }}} */
98 /* rcsid [] = "$Id$" */
100 int verbose = 1;
103 * Whether the Midnight Commander tries to provide more
104 * information about copy/move sizes and bytes transfered
105 * at the expense of some speed
107 int file_op_compute_totals = 1;
109 /* If on, it gets a little scrict with dangerous operations */
110 int know_not_what_am_i_doing = 0;
112 /* This is a hard link cache */
113 struct link {
114 struct link *next;
115 vfs *vfs;
116 dev_t dev;
117 ino_t ino;
118 short linkcount;
119 umode_t st_mode;
120 char name[1];
123 /* the hard link cache */
124 struct link *linklist = NULL;
126 /* the files-to-be-erased list */
127 struct link *erase_list;
130 * In copy_dir_dir we use two additional single linked lists: The first -
131 * variable name `parent_dirs' - holds information about already copied
132 * directories and is used to detect cyclic symbolic links.
133 * The second (`dest_dirs' below) holds information about just created
134 * target directories and is used to detect when an directory is copied
135 * into itself (we don't want to copy infinitly).
136 * Both lists don't use the linkcount and name structure members of struct
137 * link.
139 struct link *dest_dirs = 0;
141 char *op_names [3] = {
142 N_(" Copy "),
143 N_(" Move "),
144 N_(" Delete ")
147 /* }}} */
149 static int query_replace (FileOpContext *ctx, char *destname,
150 struct stat *_s_stat, struct stat *_d_stat);
151 static int query_recursive (FileOpContext *ctx, char *s);
152 static int do_file_error (char *str);
155 enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
157 static int
158 convert_case (int c, enum CaseConvs *conversion)
160 if (*conversion & UP_CHAR){
161 *conversion &= ~UP_CHAR;
162 return toupper (c);
163 } else if (*conversion & LOW_CHAR){
164 *conversion &= ~LOW_CHAR;
165 return tolower (c);
166 } else if (*conversion & UP_SECT){
167 return toupper (c);
168 } else if (*conversion & LOW_SECT){
169 return tolower (c);
170 } else
171 return c;
174 static int transform_error = 0;
176 static unsigned char *
177 do_transform_source (FileOpContext *ctx, unsigned char *source)
179 int j, k, l, len;
180 unsigned char *fnsource = x_basename (source);
181 int next_reg;
182 enum CaseConvs case_conv = NO_CONV;
183 static unsigned char fntarget [MC_MAXPATHLEN];
185 len = strlen (fnsource);
186 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
187 if (j != len){
188 transform_error = FILE_SKIP;
189 return NULL;
191 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++){
192 switch (ctx->dest_mask [j]){
193 case '\\':
194 j++;
195 if (! isdigit ((unsigned char) ctx->dest_mask [j])){
196 /* Backslash followed by non-digit */
197 switch (ctx->dest_mask [j]){
198 case 'U':
199 case_conv |= UP_SECT;
200 case_conv &= ~LOW_SECT;
201 break;
202 case 'u':
203 case_conv |= UP_CHAR;
204 break;
205 case 'L':
206 case_conv |= LOW_SECT;
207 case_conv &= ~UP_SECT;
208 break;
209 case 'l':
210 case_conv |= LOW_CHAR;
211 break;
212 case 'E':
213 case_conv = NO_CONV;
214 break;
215 default:
216 /* Backslash as quote mark */
217 fntarget [k++] = convert_case (ctx->dest_mask [j], &case_conv);
219 break;
220 } else {
221 /* Backslash followed by digit */
222 next_reg = ctx->dest_mask [j] - '0';
223 /* Fall through */
226 case '*':
227 if (next_reg < 0 || next_reg >= RE_NREGS
228 || ctx->regs.start [next_reg] < 0){
229 message_1s (1, MSG_ERROR, _(" Invalid target mask "));
230 transform_error = FILE_ABORT;
231 return NULL;
233 for (l = ctx->regs.start [next_reg]; l < ctx->regs.end [next_reg]; l++)
234 fntarget [k++] = convert_case (fnsource [l], &case_conv);
235 next_reg ++;
236 break;
238 default:
239 fntarget [k++] = convert_case (ctx->dest_mask [j], &case_conv);
240 break;
243 fntarget [k] = 0;
244 return fntarget;
247 static unsigned char *
248 transform_source (FileOpContext *ctx, unsigned char *source)
250 unsigned char *s = g_strdup (source);
251 unsigned char *q;
253 /* We remove \n from the filename since regex routines would use \n as an anchor */
254 /* this is just to be allowed to maniupulate file names with \n on it */
255 for (q = s; *q; q++){
256 if (*q == '\n')
257 *q = ' ';
259 q = do_transform_source (ctx, s);
260 g_free (s);
261 return q;
264 static void
265 free_linklist (struct link **linklist)
267 struct link *lp, *lp2;
269 for (lp = *linklist; lp != NULL; lp = lp2){
270 lp2 = lp -> next;
271 g_free (lp);
273 *linklist = NULL;
276 static int
277 is_in_linklist (struct link *lp, char *path, struct stat *sb)
279 ino_t ino = sb->st_ino;
280 dev_t dev = sb->st_dev;
281 #ifdef USE_VFS
282 vfs *vfs = vfs_type (path);
283 #endif /* USE_VFS */
285 while (lp){
286 #ifdef USE_VFS
287 if (lp->vfs == vfs)
288 #endif /* USE_VFS */
289 if (lp->ino == ino && lp->dev == dev )
290 return 1;
291 lp = lp->next;
293 return 0;
297 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
298 * and a hardlink was succesfully made
300 static int
301 check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
303 struct link *lp;
304 vfs *my_vfs = vfs_type (src_name);
305 ino_t ino = pstat->st_ino;
306 dev_t dev = pstat->st_dev;
307 struct stat link_stat;
308 char *p;
310 #if 1 /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
311 if (vfs_file_is_ftp (src_name) || vfs_file_is_smb (src_name))
312 return 0;
313 #endif
314 for (lp = linklist; lp != NULL; lp = lp -> next)
315 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev){
316 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino &&
317 link_stat.st_dev == dev && vfs_type (lp->name) == my_vfs){
318 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
319 was copied to */
320 if (vfs_type (dst_name) == vfs_type (p)){
321 if (!mc_stat (p, &link_stat)){
322 if (!mc_link (p, dst_name))
323 return 1;
327 message_1s (1, MSG_ERROR, _(" Could not make the hardlink "));
328 return 0;
330 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
331 + strlen (dst_name) + 1);
332 if (lp){
333 lp->vfs = my_vfs;
334 lp->ino = ino;
335 lp->dev = dev;
336 strcpy (lp->name, src_name);
337 p = strchr (lp->name, 0) + 1;
338 strcpy (p, dst_name);
339 lp->next = linklist;
340 linklist = lp;
342 return 0;
346 * Duplicate the contents of the symbolic link src_path in dst_path.
347 * Try to make a stable symlink if the option "stable symlink" was
348 * set in the file mask dialog.
349 * If dst_path is an existing symlink it will be deleted silently
350 * (upper levels take already care of existing files at dst_path).
352 static int
353 make_symlink (FileOpContext *ctx, char *src_path, char *dst_path)
355 char link_target[MC_MAXPATHLEN];
356 int len;
357 int return_status;
358 struct stat sb;
359 int dst_is_symlink;
361 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
362 dst_is_symlink = 1;
363 else
364 dst_is_symlink = 0;
366 retry_src_readlink:
367 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
368 if (len < 0){
369 return_status = file_error (_(" Cannot read source link \"%s\" \n %s "), src_path);
370 if (return_status == FILE_RETRY)
371 goto retry_src_readlink;
372 return return_status;
374 link_target[len] = 0;
376 if (ctx->stable_symlinks)
377 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
378 message_1s (1, MSG_ERROR,
379 _(" Cannot make stable symlinks across "
380 "non-local filesystems: \n\n"
381 " Option Stable Symlinks will be disabled "));
382 ctx->stable_symlinks = 0;
385 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
386 char *p, *q, *r, *s;
388 p = g_strdup (src_path);
389 r = strrchr (p, PATH_SEP);
391 if (r){
392 r[1] = 0;
393 if (*dst_path == PATH_SEP)
394 q = g_strdup (dst_path);
395 else
396 q = g_strconcat (p, dst_path, NULL);
397 r = strrchr (q, PATH_SEP);
398 if (r){
399 r[1] = 0;
400 s = g_strconcat (p, link_target, NULL);
401 strcpy (link_target, s);
402 g_free (s);
403 s = diff_two_paths (q, link_target);
404 if (s){
405 strcpy (link_target, s);
406 g_free (s);
409 g_free (q);
411 g_free (p);
413 retry_dst_symlink:
414 if (mc_symlink (link_target, dst_path) == 0)
415 /* Success */
416 return FILE_CONT;
418 * if dst_exists, it is obvious that this had failed.
419 * We can delete the old symlink and try again...
421 if (dst_is_symlink){
422 if (!mc_unlink (dst_path))
423 if (mc_symlink (link_target, dst_path) == 0)
424 /* Success */
425 return FILE_CONT;
427 return_status = file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path);
428 if (return_status == FILE_RETRY)
429 goto retry_dst_symlink;
430 return return_status;
433 static int
434 progress_update_one (FileOpContext *ctx,
435 off_t *progress_count,
436 double *progress_bytes,
437 int add,
438 int is_toplevel_file)
440 int ret;
442 if (is_toplevel_file || ctx->progress_totals_computed) {
443 (*progress_count)++;
444 (*progress_bytes) += add;
447 /* Apply some heuristic here to not call the update stuff very often */
448 ret = file_progress_show_count (ctx, *progress_count, ctx->progress_count);
449 if (ret != FILE_CONT)
450 return ret;
451 ret = file_progress_show_bytes (ctx, *progress_bytes, ctx->progress_bytes);
453 return ret;
457 copy_file_file (FileOpContext *ctx, char *src_path, char *dst_path, int ask_overwrite,
458 off_t *progress_count, double *progress_bytes,
459 int is_toplevel_file)
461 #ifndef NATIVE_WIN32
462 uid_t src_uid = (uid_t) -1;
463 gid_t src_gid = (gid_t) -1;
464 #endif /* !NATIVE_WIN32 */
465 char *buf = NULL;
466 int buf_size = BUF_8K;
467 int src_desc, dest_desc = 0;
468 int n_read, n_written;
469 int src_mode = 0; /* The mode of the source file */
470 struct stat sb, sb2;
471 struct utimbuf utb;
472 int dst_exists = 0, appending = 0;
473 off_t n_read_total = 0, file_size = -1;
474 int return_status, temp_status;
475 struct timeval tv_transfer_start;
477 /* bitmask used to remember which resourses we should release on return
478 A single goto label is much easier to handle than a bunch of gotos ;-). */
479 unsigned resources = 0;
481 /* FIXME: We should not be using global variables! */
482 ctx->do_reget = 0;
483 return_status = FILE_RETRY;
485 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
486 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
487 return FILE_ABORT;
489 mc_refresh ();
491 retry_dst_stat:
492 if (mc_stat (dst_path, &sb2) == 0){
493 if (S_ISDIR (sb2.st_mode)){
494 return_status = file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path);
495 if (return_status == FILE_RETRY)
496 goto retry_dst_stat;
497 return return_status;
499 dst_exists = 1;
502 while ((* ctx->stat_func) (src_path, &sb)) {
503 return_status = file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path);
504 if (return_status != FILE_RETRY)
505 return return_status;
508 if (dst_exists){
509 /* .ado: For Win32: no st_ino exists, it is better to just try to
510 * overwrite the target file
512 #ifndef NATIVE_WIN32
513 /* Destination already exists */
514 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino){
515 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file. "), src_path, dst_path);
516 do_refresh ();
517 return FILE_SKIP;
519 #endif /* !NATIVE_WIN32 */
521 /* Should we replace destination? */
522 if (ask_overwrite){
523 ctx->do_reget = 0;
524 return_status = query_replace (ctx, dst_path, &sb, &sb2);
525 if (return_status != FILE_CONT)
526 return return_status;
530 if (!ctx->do_append) {
531 /* .ado: OS2 and NT don't have hardlinks */
532 #ifndef NATIVE_WIN32
533 /* Check the hardlinks */
534 if (!ctx->follow_links && sb.st_nlink > 1 &&
535 check_hardlinks (src_path, dst_path, &sb) == 1){
536 /* We have made a hardlink - no more processing is necessary */
537 return return_status;
540 if (S_ISLNK (sb.st_mode)) {
541 int retval;
543 retval = make_symlink (ctx, src_path, dst_path);
544 return retval;
547 #endif /* !NATIVE_WIN32 */
549 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
550 || S_ISSOCK (sb.st_mode)){
551 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0){
552 return_status = file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path);
553 if (return_status == FILE_RETRY)
554 continue;
555 return return_status;
557 /* Success */
559 #ifndef NATIVE_WIN32
560 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid)){
561 temp_status = file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
562 if (temp_status == FILE_RETRY)
563 continue;
564 return temp_status;
566 #endif /* !NATIVE_WIN32 */
567 while (ctx->preserve &&
568 (mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) < 0)){
569 temp_status = file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
570 if (temp_status == FILE_RETRY)
571 continue;
572 return temp_status;
574 return FILE_CONT;
578 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
580 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0){
581 return_status = file_error (_(" Cannot open source file \"%s\" \n %s "), src_path);
582 if (return_status == FILE_RETRY)
583 continue;
584 ctx->do_append = 0;
585 return return_status;
588 resources |= 1;
589 if (ctx->do_reget){
590 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget){
591 message_1s (1, _(" Warning "), _(" Reget failed, about to overwrite file "));
592 ctx->do_reget = ctx->do_append = 0;
596 while (mc_fstat (src_desc, &sb)){
597 return_status = file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path);
598 if (return_status == FILE_RETRY)
599 continue;
600 ctx->do_append = 0;
601 goto ret;
603 src_mode = sb.st_mode;
604 #ifndef NATIVE_WIN32
605 src_uid = sb.st_uid;
606 src_gid = sb.st_gid;
607 #endif /* !NATIVE_WIN32 */
608 utb.actime = sb.st_atime;
609 utb.modtime = sb.st_mtime;
610 file_size = sb.st_size;
612 /* Create the new regular file with small permissions initially,
613 do not create a security hole. FIXME: You have security hole
614 here, btw. Imagine copying to /tmp and symlink attack :-( */
616 while ((dest_desc = mc_open (dst_path, O_WRONLY |
617 (ctx->do_append ? O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0){
618 return_status = file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path);
619 if (return_status == FILE_RETRY)
620 continue;
621 ctx->do_append = 0;
622 goto ret;
624 resources |= 2; /* dst_path exists/dst_path opened */
625 resources |= 4; /* remove short file */
627 appending = ctx->do_append;
628 ctx->do_append = 0;
630 /* Find out the optimal buffer size. */
631 while (mc_fstat (dest_desc, &sb)){
632 return_status = file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path);
633 if (return_status == FILE_RETRY)
634 continue;
635 goto ret;
637 buf = (char *) g_malloc (buf_size);
639 ctx->eta_secs = 0.0;
640 ctx->bps = 0;
642 return_status = file_progress_show (ctx, 0, file_size);
644 mc_refresh ();
646 if (return_status != FILE_CONT)
647 goto ret;
650 struct timeval tv_current, tv_last_update, tv_last_input;
651 int secs, update_secs;
652 long dt;
653 char *stalled_msg;
655 tv_last_update = tv_transfer_start;
657 for (;;){
658 /* src_read */
659 if (mc_ctl (src_desc, MCCTL_IS_NOTREADY, 0))
660 n_read = -1;
661 else
662 while ((n_read = mc_read (src_desc, buf, buf_size))<0){
663 return_status = file_error(_(" Cannot read source file \"%s\" \n %s "), src_path);
664 if (return_status == FILE_RETRY)
665 continue;
666 goto ret;
668 if (n_read == 0)
669 break;
671 gettimeofday (&tv_current, NULL);
673 if (n_read>0){
674 n_read_total += n_read;
676 /* Windows NT ftp servers report that files have no
677 * permissions: -------, so if we happen to have actually
678 * read something, we should fix the permissions.
680 if (!(src_mode &
681 ((S_IRUSR|S_IWUSR|S_IXUSR) /* user */
682 |(S_IXOTH|S_IWOTH|S_IROTH) /* other */
683 |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
684 src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
685 gettimeofday (&tv_last_input, NULL);
687 /* dst_write */
688 while ((n_written = mc_write (dest_desc, buf, n_read)) < n_read){
689 if (n_written>0){
690 n_read -= n_written;
691 continue;
693 return_status = file_error(_(" Cannot write target file \"%s\" \n %s "),
694 dst_path);
695 if (return_status == FILE_RETRY)
696 continue;
697 goto ret;
701 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
702 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
703 if (secs > 2){
704 rotate_dash ();
705 tv_last_update = tv_current;
708 /* 2. Check for a stalled condition */
709 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
710 stalled_msg = "";
711 if (update_secs > 4){
712 stalled_msg = _("(stalled)");
715 /* 3. Compute ETA */
716 if (secs > 2){
717 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
719 if (n_read_total){
720 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
721 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
722 } else
723 ctx->eta_secs = 0.0;
726 /* 4. Compute BPS rate */
727 if (secs > 2){
728 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
729 if (ctx->bps_time < 1)
730 ctx->bps_time = 1;
731 ctx->bps = n_read_total / ctx->bps_time;
734 file_progress_set_stalled_label (ctx, stalled_msg);
735 file_progress_show_bytes (ctx, *progress_bytes + n_read_total, ctx->progress_bytes);
736 return_status = file_progress_show (ctx, n_read_total, file_size);
737 mc_refresh ();
738 if (return_status != FILE_CONT)
739 goto ret;
743 resources &= ~4; /* copy successful, don't remove target file */
745 ret:
746 if (buf)
747 g_free (buf);
749 while ((resources & 1) && mc_close (src_desc) < 0){
750 temp_status = file_error (_(" Cannot close source file \"%s\" \n %s "), src_path);
751 if (temp_status == FILE_RETRY)
752 continue;
753 if (temp_status == FILE_ABORT)
754 return_status = temp_status;
755 break;
758 while ((resources & 2) && mc_close (dest_desc) < 0){
759 temp_status = file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path);
760 if (temp_status == FILE_RETRY)
761 continue;
762 return_status = temp_status;
763 break;
766 if (resources & 4){
767 /* Remove short file */
768 int result;
769 result = query_dialog (_("Copy"), _("Incomplete file was retrieved. Keep it?"),
770 D_ERROR, 2, _("&Delete"), _("&Keep"));
771 if (!result)
772 mc_unlink (dst_path);
773 } else if (resources & (2|8)){
774 /* no short file and destination file exists */
775 #ifndef NATIVE_WIN32
776 if (!appending && ctx->preserve_uidgid){
777 while (mc_chown (dst_path, src_uid, src_gid)){
778 temp_status = file_error
779 (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
780 if (temp_status == FILE_RETRY)
781 continue;
782 return_status = temp_status;
783 break;
786 #endif /* !NATIVE_WIN32 */
789 * .ado: according to the XPG4 standard, the file must be closed before
790 * chmod can be invoked
792 if (!appending && ctx->preserve){
793 while (mc_chmod (dst_path, src_mode & ctx->umask_kill)){
794 temp_status = file_error (
795 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
796 if (temp_status != FILE_RETRY){
797 return_status = temp_status;
798 break;
801 mc_utime (dst_path, &utb);
805 if (return_status == FILE_CONT)
806 return_status = progress_update_one (ctx, progress_count, progress_bytes,
807 file_size, is_toplevel_file);
809 return return_status;
813 * I think these copy_*_* functions should have a return type.
814 * anyway, this function *must* have two directories as arguments.
816 /* FIXME: This function needs to check the return values of the
817 function calls */
819 copy_dir_dir (FileOpContext *ctx, char *s, char *d, int toplevel,
820 int move_over, int delete,
821 struct link *parent_dirs,
822 off_t *progress_count,
823 double *progress_bytes)
825 struct dirent *next;
826 struct stat buf, cbuf;
827 DIR *reading;
828 char *path, *mdpath, *dest_file, *dest_dir;
829 int return_status = FILE_CONT;
830 struct utimbuf utb;
831 struct link *lp;
833 /* First get the mode of the source dir */
834 retry_src_stat:
835 if ((* ctx->stat_func) (s, &cbuf)){
836 return_status = file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
837 if (return_status == FILE_RETRY)
838 goto retry_src_stat;
839 return return_status;
842 if (is_in_linklist (dest_dirs, s, &cbuf)){
843 /* Don't copy a directory we created before (we don't want to copy
844 infinitely if a directory is copied into itself) */
845 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
846 return FILE_CONT;
849 /* Hmm, hardlink to directory??? - Norbert */
850 /* FIXME: In this step we should do something
851 in case the destination already exist */
852 /* Check the hardlinks */
853 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1){
854 /* We have made a hardlink - no more processing is necessary */
855 return return_status;
858 if (!S_ISDIR (cbuf.st_mode)){
859 return_status = file_error (_(" Source directory \"%s\" is not a directory \n %s "), s);
860 if (return_status == FILE_RETRY)
861 goto retry_src_stat;
862 return return_status;
865 if (is_in_linklist (parent_dirs, s, &cbuf)){
866 /* we found a cyclic symbolic link */
867 message_2s (1, MSG_ERROR, _(" Cannot copy cyclic symbolic link \n `%s' "), s);
868 return FILE_SKIP;
871 lp = g_new (struct link, 1);
872 lp->vfs = vfs_type (s);
873 lp->ino = cbuf.st_ino;
874 lp->dev = cbuf.st_dev;
875 lp->next = parent_dirs;
876 parent_dirs = lp;
878 retry_dst_stat:
879 /* Now, check if the dest dir exists, if not, create it. */
880 if (mc_stat (d, &buf)){
881 /* Here the dir doesn't exist : make it !*/
883 if (move_over){
884 if (mc_rename (s, d) == 0){
885 g_free (parent_dirs);
886 return FILE_CONT;
889 dest_dir = g_strdup (d);
890 } else {
892 * If the destination directory exists, we want to copy the whole
893 * directory, but we only want this to happen once.
895 * Escape sequences added to the * to compiler warnings.
896 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
897 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
899 if (!S_ISDIR (buf.st_mode)){
900 return_status = file_error (_(" Destination \"%s\" must be a directory \n %s "), d);
901 if (return_status == FILE_RETRY)
902 goto retry_dst_stat;
903 g_free (parent_dirs);
904 return return_status;
906 #if 1
907 /* Again, I'm getting curious. Is not d already what we wanted, incl.
908 * masked source basename? Is not this just a relict of the past versions?
909 * I'm afraid this will lead into a two level deep dive :(
911 * I think this is indeed the problem. I can not remember any case where
912 * we actually would like that behaviour -miguel
914 * It's a documented feature (option `Dive into subdir if exists' in the
915 * copy/move dialog). -Norbert
917 if (toplevel && ctx->dive_into_subdirs){
918 dest_dir = concat_dir_and_file (d, x_basename (s));
919 } else
920 #endif
922 dest_dir = g_strdup (d);
923 goto dont_mkdir;
926 retry_dst_mkdir:
927 if (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)){
928 return_status = file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir);
929 if (return_status == FILE_RETRY)
930 goto retry_dst_mkdir;
931 goto ret;
934 lp = g_new (struct link, 1);
935 mc_stat (dest_dir, &buf);
936 lp->vfs = vfs_type (dest_dir);
937 lp->ino = buf.st_ino;
938 lp->dev = buf.st_dev;
939 lp->next = dest_dirs;
940 dest_dirs = lp;
942 #ifndef NATIVE_WIN32
943 if (ctx->preserve_uidgid){
944 retry_dst_chown:
945 if (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)){
946 return_status = file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
947 if (return_status == FILE_RETRY)
948 goto retry_dst_chown;
949 goto ret;
952 #endif /* !NATIVE_WIN32 */
954 dont_mkdir:
955 /* open the source dir for reading */
956 if ((reading = mc_opendir (s)) == 0){
957 goto ret;
960 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
962 * Now, we don't want '.' and '..' to be created / copied at any time
964 if (!strcmp (next->d_name, "."))
965 continue;
966 if (!strcmp (next->d_name, ".."))
967 continue;
969 /* get the filename and add it to the src directory */
970 path = concat_dir_and_file (s, next->d_name);
972 (* ctx->stat_func) (path, &buf);
973 if (S_ISDIR (buf.st_mode)){
974 mdpath = concat_dir_and_file (dest_dir, next->d_name);
976 * From here, we just intend to recursively copy subdirs, not
977 * the double functionality of copying different when the target
978 * dir already exists. So, we give the recursive call the flag 0
979 * meaning no toplevel.
981 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0,
982 delete, parent_dirs, progress_count, progress_bytes);
983 g_free (mdpath);
984 } else {
985 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
986 return_status = copy_file_file (ctx, path, dest_file, 1,
987 progress_count, progress_bytes, 0);
988 g_free (dest_file);
990 if (delete && return_status == FILE_CONT){
991 if (ctx->erase_at_end){
992 static struct link *tail;
993 lp = g_malloc (sizeof (struct link) + strlen (path));
994 strcpy (lp->name, path);
995 lp->st_mode = buf.st_mode;
996 lp->next = 0;
997 if (erase_list){
998 tail->next = lp;
999 tail = lp;
1000 } else
1001 erase_list = tail = lp;
1002 } else {
1003 if (S_ISDIR (buf.st_mode)){
1004 return_status = erase_dir_iff_empty (ctx, path);
1005 } else
1006 return_status = erase_file (ctx, path, 0, 0, 0);
1010 g_free (path);
1012 mc_closedir (reading);
1014 if (ctx->preserve){
1015 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1016 utb.actime = cbuf.st_atime;
1017 utb.modtime = cbuf.st_mtime;
1018 mc_utime(dest_dir, &utb);
1021 ret:
1022 g_free (dest_dir);
1023 g_free (parent_dirs);
1024 return return_status;
1027 /* }}} */
1029 /* {{{ Move routines */
1032 move_file_file (FileOpContext *ctx, char *s, char *d,
1033 off_t *progress_count, double *progress_bytes)
1035 struct stat src_stats, dst_stats;
1036 int return_status = FILE_CONT;
1038 if (file_progress_show_source (ctx, s) == FILE_ABORT
1039 || file_progress_show_target (ctx, d) == FILE_ABORT)
1040 return FILE_ABORT;
1042 mc_refresh ();
1044 retry_src_lstat:
1045 if (mc_lstat (s, &src_stats) != 0){
1046 /* Source doesn't exist */
1047 return_status = file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1048 if (return_status == FILE_RETRY)
1049 goto retry_src_lstat;
1050 return return_status;
1053 if (mc_lstat (d, &dst_stats) == 0){
1054 /* Destination already exists */
1055 /* .ado: for Win32, no st_ino exists */
1056 #ifndef NATIVE_WIN32
1057 if (src_stats.st_dev == dst_stats.st_dev
1058 && src_stats.st_ino == dst_stats.st_ino){
1059 int msize = COLS - 36;
1060 char st[MC_MAXPATHLEN];
1061 char dt[MC_MAXPATHLEN];
1063 if (msize < 0)
1064 msize = 40;
1065 msize /= 2;
1067 strcpy (st, name_trunc (s, msize));
1068 strcpy (dt, name_trunc (d, msize));
1069 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file "),
1070 st, dt );
1071 do_refresh ();
1072 return FILE_SKIP;
1074 #endif /* !NATIVE_WIN32 */
1075 if (S_ISDIR (dst_stats.st_mode)){
1076 message_2s (1, MSG_ERROR, _(" Cannot overwrite directory `%s' "), d);
1077 do_refresh ();
1078 return FILE_SKIP;
1081 if (confirm_overwrite){
1082 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1083 if (return_status != FILE_CONT)
1084 return return_status;
1086 /* Ok to overwrite */
1089 if (!ctx->do_append) {
1090 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1091 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1092 goto retry_src_remove;
1093 } else
1094 return return_status;
1097 if (mc_rename (s, d) == 0){
1098 return FILE_CONT;
1101 #if 0
1102 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1103 one nfs to the same, but on the server it is on two different
1104 filesystems. Then nfs returns EIO instead of EXDEV.
1105 Hope it will not hurt if we always in case of error try to copy/delete. */
1106 else
1107 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1109 if (errno != EXDEV){
1110 return_status = files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s, d);
1111 if (return_status == FILE_RETRY)
1112 goto retry_rename;
1113 return return_status;
1115 #endif
1117 /* Failed because filesystem boundary -> copy the file instead */
1118 return_status = copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1119 if (return_status != FILE_CONT)
1120 return return_status;
1122 if ((return_status = file_progress_show_source (ctx, NULL)) != FILE_CONT
1123 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1124 return return_status;
1126 mc_refresh ();
1128 retry_src_remove:
1129 if (mc_unlink (s)){
1130 return_status = file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1131 if (return_status == FILE_RETRY)
1132 goto retry_src_remove;
1133 return return_status;
1136 if (return_status == FILE_CONT)
1137 return_status = progress_update_one (ctx,
1138 progress_count,
1139 progress_bytes, src_stats.st_size, 1);
1141 return return_status;
1145 move_dir_dir (FileOpContext *ctx, char *s, char *d,
1146 off_t *progress_count, double *progress_bytes)
1148 struct stat sbuf, dbuf, destbuf;
1149 struct link *lp;
1150 char *destdir;
1151 int return_status;
1152 int move_over = 0;
1154 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1155 file_progress_show_target (ctx, d) == FILE_ABORT)
1156 return FILE_ABORT;
1158 mc_refresh ();
1160 mc_stat (s, &sbuf);
1161 if (mc_stat (d, &dbuf))
1162 destdir = g_strdup (d); /* destination doesn't exist */
1163 else if (!ctx->dive_into_subdirs){
1164 destdir = g_strdup (d);
1165 move_over = 1;
1166 } else
1167 destdir = concat_dir_and_file (d, x_basename (s));
1168 #ifndef NATIVE_WIN32
1169 if (sbuf.st_dev == dbuf.st_dev
1170 && sbuf.st_ino == dbuf.st_ino){
1171 int msize = COLS - 36;
1172 char st[MC_MAXPATHLEN];
1173 char dt[MC_MAXPATHLEN];
1175 if (msize < 0)
1176 msize = 40;
1177 msize /= 2;
1179 strcpy (st, name_trunc (s, msize));
1180 strcpy (dt, name_trunc (d, msize));
1181 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same directory "),
1182 st, dt );
1183 do_refresh ();
1184 return FILE_SKIP;
1186 #endif /* !NATIVE_WIN32 */
1188 /* Check if the user inputted an existing dir */
1189 retry_dst_stat:
1190 if (!mc_stat (destdir, &destbuf)){
1191 if (move_over){
1192 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1193 progress_count, progress_bytes);
1195 if (return_status != FILE_CONT)
1196 goto ret;
1197 goto oktoret;
1198 } else {
1199 if (S_ISDIR (destbuf.st_mode))
1200 return_status = file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir);
1201 else
1202 return_status = file_error (_(" Cannot overwrite file \"%s\" %s "), destdir);
1203 if (return_status == FILE_RETRY)
1204 goto retry_dst_stat;
1206 g_free (destdir);
1207 return return_status;
1210 retry_rename:
1211 if (mc_rename (s, destdir) == 0){
1212 return_status = FILE_CONT;
1213 goto ret;
1215 /* .ado: Drive, Do we need this anymore? */
1216 #ifdef WIN32
1217 else {
1218 /* EXDEV: cross device; does not work everywhere */
1219 if (toupper(s[0]) != toupper(destdir[0]))
1220 goto w32try;
1222 #endif /* WIN32 */
1224 if (errno != EXDEV){
1225 return_status = files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s, d);
1226 if (return_status == FILE_RETRY)
1227 goto retry_rename;
1228 goto ret;
1230 #ifdef WIN32
1231 w32try:
1232 #endif /* WIN32 */
1233 /* Failed because of filesystem boundary -> copy dir instead */
1234 return_status = copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count, progress_bytes);
1236 if (return_status != FILE_CONT)
1237 goto ret;
1238 oktoret:
1239 if ((return_status = file_progress_show_source (ctx, NULL)) != FILE_CONT
1240 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1241 goto ret;
1243 mc_refresh ();
1244 if (ctx->erase_at_end){
1245 for (; erase_list && return_status != FILE_ABORT;){
1246 if (S_ISDIR (erase_list->st_mode)){
1247 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1248 } else
1249 return_status = erase_file (ctx, erase_list->name, 0, 0, 0);
1250 lp = erase_list;
1251 erase_list = erase_list->next;
1252 g_free (lp);
1255 erase_dir_iff_empty (ctx, s);
1257 ret:
1258 g_free (destdir);
1259 while (erase_list){
1260 lp = erase_list;
1261 erase_list = erase_list->next;
1262 g_free (lp);
1264 return return_status;
1267 /* }}} */
1269 /* {{{ Erase routines */
1270 /* Don't update progress status if progress_count==NULL */
1272 erase_file (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes,
1273 int is_toplevel_file)
1275 int return_status;
1276 struct stat buf;
1278 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1279 return FILE_ABORT;
1280 mc_refresh ();
1282 if (progress_count && mc_lstat (s, &buf)) {
1283 /* ignore, most likely the mc_unlink fails, too */
1284 buf.st_size = 0;
1287 while (mc_unlink (s)){
1288 return_status = file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1289 if (return_status != FILE_RETRY)
1290 return return_status;
1293 if (progress_count)
1294 return progress_update_one (ctx, progress_count, progress_bytes, buf.st_size,
1295 is_toplevel_file);
1296 else
1297 return FILE_CONT;
1300 static int
1301 recursive_erase (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes)
1303 struct dirent *next;
1304 struct stat buf;
1305 DIR *reading;
1306 char *path;
1307 int return_status = FILE_CONT;
1309 if (!strcmp (s, ".."))
1310 return 1;
1312 reading = mc_opendir (s);
1314 if (!reading)
1315 return 1;
1317 while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
1318 if (!strcmp (next->d_name, "."))
1319 continue;
1320 if (!strcmp (next->d_name, ".."))
1321 continue;
1322 path = concat_dir_and_file (s, next->d_name);
1323 if (mc_lstat (path, &buf)){
1324 g_free (path);
1325 mc_closedir (reading);
1326 return 1;
1328 if (S_ISDIR (buf.st_mode))
1329 return_status = (recursive_erase (ctx, path, progress_count, progress_bytes)
1330 != FILE_CONT);
1331 else
1332 return_status = erase_file (ctx, path, progress_count, progress_bytes, 0);
1333 g_free (path);
1335 mc_closedir (reading);
1336 if (return_status != FILE_CONT)
1337 return return_status;
1338 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1339 return FILE_ABORT;
1340 mc_refresh ();
1342 while (my_rmdir (s)){
1343 return_status = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1344 if (return_status != FILE_RETRY)
1345 return return_status;
1348 return FILE_CONT;
1351 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1352 in the directory path points to, 0 else. */
1353 static int
1354 check_dir_is_empty(char *path)
1356 DIR *dir;
1357 struct dirent *d;
1358 int i;
1360 dir = mc_opendir (path);
1361 if (!dir)
1362 return -1;
1364 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)){
1365 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1366 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1367 continue; /* "." or ".." */
1368 i = 0;
1369 break;
1372 mc_closedir (dir);
1373 return i;
1377 erase_dir (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes)
1379 int error;
1381 if (strcmp (s, "..") == 0)
1382 return FILE_SKIP;
1384 if (strcmp (s, ".") == 0)
1385 return FILE_SKIP;
1387 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1388 return FILE_ABORT;
1389 mc_refresh ();
1391 /* The old way to detect a non empty directory was:
1392 error = my_rmdir (s);
1393 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1394 For the linux user space nfs server (nfs-server-2.2beta29-2)
1395 we would have to check also for EIO. I hope the new way is
1396 fool proof. (Norbert)
1398 error = check_dir_is_empty (s);
1399 if (error == 0){ /* not empty */
1400 error = query_recursive (ctx, s);
1401 if (error == FILE_CONT)
1402 return recursive_erase (ctx, s, progress_count, progress_bytes);
1403 else
1404 return error;
1407 while (my_rmdir (s) == -1){
1408 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1409 if (error != FILE_RETRY)
1410 return error;
1413 return FILE_CONT;
1417 erase_dir_iff_empty (FileOpContext *ctx, char *s)
1419 int error;
1421 if (strcmp (s, "..") == 0)
1422 return FILE_SKIP;
1424 if (strcmp (s, ".") == 0)
1425 return FILE_SKIP;
1427 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1428 return FILE_ABORT;
1429 mc_refresh ();
1431 if (1 != check_dir_is_empty (s)) /* not empty or error */
1432 return FILE_CONT;
1434 while (my_rmdir (s)){
1435 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1436 if (error != FILE_RETRY)
1437 return error;
1440 return FILE_CONT;
1443 /* }}} */
1445 /* {{{ Panel operate routines */
1447 /* Returns currently selected file or the first marked file if there is one */
1448 char *
1449 panel_get_file (WPanel *panel, struct stat *stat_buf)
1451 int i;
1453 /* No problem with Gnome, as get_current_type never returns view_tree there */
1454 if (get_current_type () == view_tree){
1455 WTree *tree = (WTree *)get_panel_widget (get_current_index ());
1457 mc_stat (tree->selected_ptr->name, stat_buf);
1458 return tree->selected_ptr->name;
1461 if (panel->marked){
1462 for (i = 0; i < panel->count; i++)
1463 if (panel->dir.list [i].f.marked){
1464 *stat_buf = panel->dir.list [i].buf;
1465 return panel->dir.list [i].fname;
1467 } else {
1468 *stat_buf = panel->dir.list [panel->selected].buf;
1469 return panel->dir.list [panel->selected].fname;
1471 g_assert_not_reached ();
1472 return NULL;
1476 is_wildcarded (char *p)
1478 for (; *p; p++){
1479 if (*p == '*')
1480 return 1;
1481 else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
1482 return 1;
1484 return 0;
1488 * compute_dir_size:
1490 * Computes the number of bytes used by the files in a directory
1492 void
1493 compute_dir_size (char *dirname, off_t *ret_marked, double *ret_total)
1495 DIR *dir;
1496 struct dirent *dirent;
1498 dir = mc_opendir (dirname);
1500 if (!dir)
1501 return;
1503 while ((dirent = mc_readdir (dir)) != NULL){
1504 struct stat s;
1505 char *fullname;
1506 int res;
1508 if (strcmp (dirent->d_name, ".") == 0)
1509 continue;
1510 if (strcmp (dirent->d_name, "..") == 0)
1511 continue;
1513 fullname = concat_dir_and_file (dirname, dirent->d_name);
1515 res = mc_lstat (fullname, &s);
1517 if (res != 0){
1518 g_free (fullname);
1519 continue;
1522 if (S_ISDIR (s.st_mode)){
1523 off_t subdir_count = 0;
1524 double subdir_bytes = 0;
1526 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1528 *ret_marked += subdir_count;
1529 *ret_total += subdir_bytes;
1530 } else {
1531 (*ret_marked)++;
1532 *ret_total += s.st_size;
1534 g_free (fullname);
1537 mc_closedir (dir);
1541 * panel_compute_totals:
1543 * compute the number of files and the number of bytes
1544 * used up by the whole selection, recursing directories
1545 * as required. In addition, it checks to see if it will
1546 * overwrite any files by doing the copy.
1548 static void
1549 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1551 int i;
1553 *ret_marked = 0;
1554 *ret_total = 0.0;
1556 for (i = 0; i < panel->count; i++){
1557 struct stat *s;
1559 if (!panel->dir.list [i].f.marked)
1560 continue;
1562 s = &panel->dir.list [i].buf;
1564 if (S_ISDIR (s->st_mode)){
1565 char *dir_name;
1566 off_t subdir_count = 0;
1567 double subdir_bytes = 0;
1569 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list [i].fname);
1570 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1572 *ret_marked += subdir_count;
1573 *ret_total += subdir_bytes;
1574 g_free (dir_name);
1575 } else {
1576 (*ret_marked)++;
1577 *ret_total += s->st_size;
1583 * This array introduced to avoid translation problems. The former (op_names)
1584 * is assumed to be nouns, suitable in dialog box titles; this one should
1585 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1586 * Notice first symbol - it is to fool gettext and force these strings to
1587 * be different for it. First symbol is skipped while building a prompt.
1588 * (I don't use spaces around the words, because someday they could be
1589 * dropped, when widgets get smarter)
1591 static char *op_names1 [] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1592 #define FMD_XLEN 64
1594 int fmd_xlen = FMD_XLEN;
1597 * These are formats for building a prompt. Parts encoded as follows:
1598 * %o - operation from op_names1
1599 * %f - file/files or files/directories, as appropriate
1600 * %m - "with source mask" or question mark for delete
1601 * %s - source name (truncated)
1602 * %d - number of marked files
1603 * %e - "to:" or question mark for delete
1605 * xgettext:no-c-format */
1606 static char* one_format = N_("%o %f \"%s\"%m");
1607 /* xgettext:no-c-format */
1608 static char* many_format = N_("%o %d %f%m");
1609 static char* prompt_parts [] =
1611 N_("file"), N_("files"), N_("directory"), N_("directories"),
1612 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1615 static char *
1616 panel_operate_generate_prompt (WPanel* panel, int operation, int only_one,
1617 struct stat* src_stat)
1619 register char *sp, *cp;
1620 register int i;
1621 char format_string [BUF_MEDIUM];
1622 char *dp = format_string;
1623 char* source = NULL;
1625 #ifdef ENABLE_NLS
1626 static int i18n_flag = 0;
1627 if (!i18n_flag)
1629 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1631 for (i = sizeof (op_names1) / sizeof (op_names1 [0]); i--;)
1632 op_names1 [i] = _(op_names1 [i]);
1634 for (i = sizeof (prompt_parts) / sizeof (prompt_parts [0]); i--;)
1635 prompt_parts [i] = _(prompt_parts [i]);
1637 one_format = _(one_format);
1638 many_format = _(many_format);
1639 i18n_flag = 1;
1641 #endif /* ENABLE_NLS */
1643 sp = only_one ? one_format : many_format;
1645 if (only_one)
1646 source = panel_get_file (panel, src_stat);
1648 while (*sp)
1650 switch (*sp)
1652 case '%':
1653 cp = NULL;
1654 switch (sp[1])
1656 case 'o':
1657 cp = op_names1 [operation] + 1;
1658 break;
1659 case 'm':
1660 cp = operation == OP_DELETE ? "?" : prompt_parts [5];
1661 break;
1662 case 'e':
1663 cp = operation == OP_DELETE ? "?" : prompt_parts [6];
1664 break;
1665 case 'f':
1666 if (only_one)
1668 cp = S_ISDIR (src_stat->st_mode) ?
1669 prompt_parts [2] : prompt_parts [0];
1671 else
1673 cp = (panel->marked == panel->dirs_marked)
1674 ? prompt_parts [3]
1675 : (panel->dirs_marked ? prompt_parts [4]
1676 : prompt_parts [1]);
1678 break;
1679 default:
1680 *dp++ = *sp++;
1682 if (cp)
1684 sp += 2;
1685 while (*cp)
1686 *dp++ = *cp++;
1688 break;
1689 default:
1690 *dp++ = *sp++;
1693 *dp = '\0';
1695 if (only_one)
1697 i = fmd_xlen - strlen(format_string) - 4;
1698 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string, name_trunc (source, i));
1700 else
1702 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string, panel->marked);
1703 i = strlen (cmd_buf) + 6 - fmd_xlen;
1704 if (i > 0)
1706 fmd_xlen += i;
1707 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1711 return source;
1715 * panel_operate:
1717 * Performs one of the operations on the selection on the source_panel
1718 * (copy, delete, move).
1720 * Returns 1 if did change the directory
1721 * structure, Returns 0 if user aborted
1724 panel_operate (void *source_panel, FileOperation operation, char *thedefault, int ask_user)
1726 WPanel *panel = source_panel;
1727 #ifdef WITH_FULL_PATHS
1728 char *source_with_path = NULL;
1729 #else
1730 # define source_with_path source
1731 #endif /* !WITH_FULL_PATHS */
1732 char *source = NULL;
1733 char *dest = NULL;
1734 char *temp = NULL;
1735 char *save_cwd = NULL, *save_dest = NULL;
1736 int only_one = (get_current_type () == view_tree) || (panel->marked <= 1);
1737 struct stat src_stat, dst_stat;
1738 int i, value;
1739 FileOpContext *ctx;
1741 off_t count = 0;
1742 double bytes = 0;
1744 int dst_result;
1745 int do_bg; /* do background operation? */
1747 ctx = file_op_context_new ();
1749 do_bg = 0;
1750 ctx->rx.buffer = NULL;
1751 free_linklist (&linklist);
1752 free_linklist (&dest_dirs);
1753 if (get_current_type () == view_listing)
1754 if (!panel->marked && !strcmp (selection (panel)->fname, "..")){
1755 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1756 file_op_context_destroy (ctx);
1757 return 0;
1760 if (operation < OP_COPY || operation > OP_DELETE) {
1761 file_op_context_destroy (ctx);
1762 return 0;
1765 /* Generate confirmation prompt */
1766 source = panel_operate_generate_prompt (panel, operation, only_one, &src_stat);
1768 /* Show confirmation dialog */
1769 if (operation == OP_DELETE && confirm_delete){
1770 if (know_not_what_am_i_doing)
1771 query_set_sel (1);
1773 i = query_dialog (_(op_names [operation]), cmd_buf,
1774 D_ERROR, 2, _("&Yes"), _("&No"));
1776 if (i != 0) {
1777 file_op_context_destroy (ctx);
1778 return 0;
1780 } else if (operation != OP_DELETE){
1781 ctx->rx.buffer = (char *) g_malloc (MC_MAXPATHLEN);
1782 ctx->rx.allocated = MC_MAXPATHLEN;
1783 ctx->rx.translate = 0;
1785 if (ask_user){
1786 char *dest_dir;
1788 if (thedefault != NULL)
1789 dest_dir = thedefault;
1790 else if (get_other_type () == view_listing)
1791 dest_dir = opanel->cwd;
1792 else
1793 dest_dir = panel->cwd;
1795 dest = file_mask_dialog (ctx, operation, cmd_buf, dest_dir, only_one, &do_bg);
1796 if (!dest){
1797 g_free (ctx->rx.buffer);
1798 file_op_context_destroy (ctx);
1799 return 0;
1801 if (!*dest){
1802 g_free (ctx->rx.buffer);
1803 file_op_context_destroy (ctx);
1804 g_free (dest);
1805 return 0;
1807 } else {
1808 char *all = "^\\(.*\\)$";
1810 re_compile_pattern (all, strlen (all), &ctx->rx);
1811 ctx->dest_mask = g_strdup ("*");
1812 do_bg = FALSE;
1813 dest = g_strdup (thedefault);
1817 #ifdef WITH_BACKGROUND
1818 /* Did the user select to do a background operation? */
1819 if (do_bg){
1820 int v;
1822 v = do_background (ctx, g_strconcat (op_names [operation], ": ", panel->cwd, NULL));
1823 if (v == -1){
1824 message (1, MSG_ERROR, _(" Sorry, I could not put the job in background "));
1827 /* If we are the parent */
1828 if (v == 1){
1829 mc_setctl (panel->cwd, MCCTL_FORGET_ABOUT, NULL);
1830 mc_setctl (dest, MCCTL_FORGET_ABOUT, NULL);
1831 /* file_op_context_destroy (ctx); */
1832 return 0;
1835 #endif /* WITH_BACKGROUND */
1837 /* Initialize things */
1838 /* We do not want to trash cache every time file is
1839 created/touched. However, this will make our cache contain
1840 invalid data. */
1841 if (dest) {
1842 if (mc_setctl (dest, MCCTL_WANT_STALE_DATA, NULL))
1843 save_dest = g_strdup (dest);
1845 if (panel->cwd) {
1846 if (mc_setctl (panel->cwd, MCCTL_WANT_STALE_DATA, NULL))
1847 save_cwd = g_strdup (panel->cwd);
1850 /* Now, let's do the job */
1852 if (do_bg)
1853 ctx->ui = NULL;
1854 else
1855 file_op_context_create_ui (ctx, operation, 1);
1857 /* This code is only called by the tree and panel code */
1858 if (only_one){
1859 /* We now have ETA in all cases */
1861 /* One file: FIXME mc_chdir will take user out of any vfs */
1862 if (operation != OP_COPY && get_current_type () == view_tree)
1863 mc_chdir (PATH_SEP_STR);
1865 /* The source and src_stat variables have been initialized before */
1866 #ifdef WITH_FULL_PATHS
1867 source_with_path = concat_dir_and_file (panel->cwd, source);
1868 #endif /* WITH_FULL_PATHS */
1870 if (operation == OP_DELETE) {
1871 if (S_ISDIR (src_stat.st_mode))
1872 value = erase_dir (ctx, source_with_path, &count, &bytes);
1873 else
1874 value = erase_file (ctx, source_with_path, &count, &bytes, 1);
1875 } else {
1876 temp = transform_source (ctx, source_with_path);
1878 if (temp == NULL){
1879 value = transform_error;
1880 } else {
1881 temp = concat_dir_and_file (dest, temp);
1882 g_free (dest);
1883 dest = temp;
1884 temp = 0;
1886 switch (operation) {
1887 case OP_COPY:
1889 * we use file_mask_op_follow_links only with OP_COPY,
1891 (* ctx->stat_func) (source_with_path, &src_stat);
1893 if (S_ISDIR (src_stat.st_mode))
1894 value = copy_dir_dir (ctx, source_with_path, dest, 1, 0, 0, 0,
1895 &count, &bytes);
1896 else
1897 value = copy_file_file (ctx, source_with_path, dest, 1, &count, &bytes, 1);
1898 break;
1900 case OP_MOVE:
1901 if (S_ISDIR (src_stat.st_mode))
1902 value = move_dir_dir (ctx, source_with_path, dest, &count, &bytes);
1903 else
1904 value = move_file_file (ctx, source_with_path, dest, &count, &bytes);
1905 break;
1907 default:
1908 value = FILE_CONT;
1909 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1912 } /* Copy or move operation */
1914 if (value == FILE_CONT)
1915 unmark_files (panel);
1916 } else {
1917 /* Many files */
1918 /* Check destination for copy or move operation */
1919 if (operation != OP_DELETE){
1920 retry_many_dst_stat:
1921 dst_result = mc_stat (dest, &dst_stat);
1922 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
1923 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
1924 dest) == FILE_RETRY)
1925 goto retry_many_dst_stat;
1926 goto clean_up;
1930 /* Initialize variables for progress bars */
1931 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1932 panel_compute_totals (panel, &ctx->progress_count, &ctx->progress_bytes);
1933 ctx->progress_totals_computed = 1;
1934 } else {
1935 ctx->progress_totals_computed = 0;
1936 ctx->progress_count = panel->marked;
1937 ctx->progress_bytes = panel->total;
1940 /* Loop for every file, perform the actual copy operation */
1941 for (i = 0; i < panel->count; i++) {
1942 if (!panel->dir.list [i].f.marked)
1943 continue; /* Skip the unmarked ones */
1945 source = panel->dir.list [i].fname;
1946 src_stat = panel->dir.list [i].buf;
1948 #ifdef WITH_FULL_PATHS
1949 if (source_with_path)
1950 g_free (source_with_path);
1951 source_with_path = concat_dir_and_file (panel->cwd, source);
1952 #endif /* WITH_FULL_PATHS */
1954 if (operation == OP_DELETE){
1955 if (S_ISDIR (src_stat.st_mode))
1956 value = erase_dir (ctx, source_with_path, &count, &bytes);
1957 else
1958 value = erase_file (ctx, source_with_path, &count, &bytes, 1);
1959 } else {
1960 if (temp)
1961 g_free (temp);
1963 temp = transform_source (ctx, source_with_path);
1964 if (temp == NULL)
1965 value = transform_error;
1966 else {
1967 temp = concat_dir_and_file (dest, temp);
1969 switch (operation){
1970 case OP_COPY:
1972 * we use file_mask_op_follow_links only with OP_COPY,
1974 (* ctx->stat_func) (source_with_path, &src_stat);
1975 if (S_ISDIR (src_stat.st_mode))
1976 value = copy_dir_dir (ctx, source_with_path, temp, 1, 0, 0, 0,
1977 &count, &bytes);
1978 else
1979 value = copy_file_file (ctx, source_with_path, temp, 1,
1980 &count, &bytes, 1);
1981 free_linklist (&dest_dirs);
1982 break;
1984 case OP_MOVE:
1985 if (S_ISDIR (src_stat.st_mode))
1986 value = move_dir_dir (ctx, source_with_path, temp, &count, &bytes);
1987 else
1988 value = move_file_file (ctx, source_with_path, temp, &count, &bytes);
1989 break;
1991 default:
1992 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1993 goto clean_up;
1996 } /* Copy or move operation */
1998 if (value == FILE_ABORT)
1999 goto clean_up;
2001 if (value == FILE_CONT)
2002 do_file_mark (panel, i, 0);
2004 if (file_progress_show_count (ctx, count, ctx->progress_count) == FILE_ABORT)
2005 goto clean_up;
2007 if (verbose && file_progress_show_bytes (ctx, bytes, ctx->progress_bytes) == FILE_ABORT)
2008 goto clean_up;
2010 if (operation != OP_DELETE && verbose && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2011 goto clean_up;
2013 mc_refresh ();
2014 } /* Loop for every file */
2015 } /* Many files */
2016 clean_up:
2017 /* Clean up */
2019 if (save_cwd) {
2020 mc_setctl (save_cwd, MCCTL_NO_STALE_DATA, NULL);
2021 g_free (save_cwd);
2023 if (save_dest) {
2024 mc_setctl (save_dest, MCCTL_NO_STALE_DATA, NULL);
2025 g_free (save_dest);
2028 free_linklist (&linklist);
2029 free_linklist (&dest_dirs);
2030 #ifdef WITH_FULL_PATHS
2031 if (source_with_path)
2032 g_free (source_with_path);
2033 #endif /* WITH_FULL_PATHS */
2035 if (dest)
2036 g_free (dest);
2038 if (temp)
2039 g_free (temp);
2041 if (ctx->rx.buffer) {
2042 g_free (ctx->rx.buffer);
2043 ctx->rx.buffer = NULL;
2046 if (ctx->dest_mask) {
2047 g_free (ctx->dest_mask);
2048 ctx->dest_mask = NULL;
2051 #ifdef WITH_BACKGROUND
2052 /* Let our parent know we are saying bye bye */
2053 if (we_are_background) {
2054 vfs_shut ();
2055 tell_parent (MSG_CHILD_EXITING);
2056 _exit (1);
2058 #endif /* WITH_BACKGROUND */
2060 file_op_context_destroy (ctx);
2061 return 1;
2064 /* }}} */
2066 /* {{{ Query/status report routines */
2068 static int
2069 real_do_file_error (enum OperationMode mode, char *error)
2071 int result;
2072 char *msg;
2074 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2075 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2077 switch (result){
2078 case 0:
2079 do_refresh ();
2080 return FILE_SKIP;
2082 case 1:
2083 do_refresh ();
2084 return FILE_RETRY;
2086 case 2:
2087 default:
2088 return FILE_ABORT;
2092 /* Report error with one file */
2094 file_error (char *format, char *file)
2096 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2097 name_trunc (file, 30), unix_error_string (errno));
2099 return do_file_error (cmd_buf);
2102 /* Report error with two files */
2104 files_error (char *format, char *file1, char *file2)
2106 char nfile1 [16];
2107 char nfile2 [16];
2109 strcpy (nfile1, name_trunc (file1, 15));
2110 strcpy (nfile2, name_trunc (file2, 15));
2112 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2113 unix_error_string (errno));
2115 return do_file_error (cmd_buf);
2118 static int
2119 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, char *s)
2121 char *confirm;
2122 gchar *text;
2124 if (ctx->recursive_result < RECURSIVE_ALWAYS){
2125 char *msg =
2126 mode == Foreground ? _("\n Directory not empty. \n Delete it recursively? ")
2127 : _("\n Background process: Directory not empty \n Delete it recursively? ");
2128 text = g_strconcat (_(" Delete: "), name_trunc (s, 30), " ", NULL);
2130 if (know_not_what_am_i_doing)
2131 query_set_sel (1);
2132 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2133 _("&Yes"), _("&No"),
2134 _("a&ll"), _("non&E"),
2135 _("&Abort"));
2137 if (ctx->recursive_result != RECURSIVE_ABORT)
2138 do_refresh ();
2139 g_free (text);
2140 if (know_not_what_am_i_doing){
2141 if (ctx->recursive_result == RECURSIVE_YES ||
2142 ctx->recursive_result == RECURSIVE_ALWAYS){
2143 text = g_strconcat (
2144 _(" Type 'yes' if you REALLY want to delete "),
2145 ctx->recursive_result == RECURSIVE_YES
2146 ? name_trunc (s, 19) : _("all the directories "), " ", NULL);
2147 confirm = input_dialog (
2148 mode == Foreground ? _(" Recursive Delete ")
2149 : _(" Background process: Recursive Delete "),
2150 text, _("no"));
2151 do_refresh ();
2152 if (!confirm || strcmp (confirm, _("yes")))
2153 ctx->recursive_result = RECURSIVE_NEVER;
2154 g_free (confirm);
2155 g_free (text);
2160 switch (ctx->recursive_result){
2161 case RECURSIVE_YES:
2162 case RECURSIVE_ALWAYS:
2163 return FILE_CONT;
2165 case RECURSIVE_NO:
2166 case RECURSIVE_NEVER:
2167 return FILE_SKIP;
2169 case RECURSIVE_ABORT:
2171 default:
2172 return FILE_ABORT;
2176 #ifdef WITH_BACKGROUND
2177 static int
2178 do_file_error (char *str)
2180 if (we_are_background)
2181 return parent_call (real_do_file_error, NULL, 1, strlen (str), str);
2182 else
2183 return real_do_file_error (Foreground, str);
2186 static int
2187 query_recursive (FileOpContext *ctx, char *s)
2189 if (we_are_background)
2190 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2191 else
2192 return real_query_recursive (ctx, Foreground, s);
2195 static int
2196 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat, struct stat *_d_stat)
2198 if (we_are_background)
2199 return parent_call ((void *)file_progress_real_query_replace,
2200 ctx,
2202 strlen (destname), destname,
2203 sizeof (struct stat), _s_stat,
2204 sizeof(struct stat), _d_stat);
2205 else
2206 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2209 #else
2210 static int
2211 do_file_error (char *str)
2213 return real_do_file_error (Foreground, str);
2216 static int
2217 query_recursive (FileOpContext *ctx, char *s)
2219 return real_query_recursive (ctx, Foreground, s);
2222 static int
2223 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat, struct stat *_d_stat)
2225 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2228 #endif /* !WITH_BACKGROUND */
2231 Cause emacs to enter folding mode for this file:
2232 Local variables:
2233 end: